obsarray 0.2.4__tar.gz → 0.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. {obsarray-0.2.4 → obsarray-0.2.5}/.gitignore +2 -2
  2. {obsarray-0.2.4/obsarray.egg-info → obsarray-0.2.5}/PKG-INFO +5 -4
  3. obsarray-0.2.5/docs/conf.py +181 -0
  4. obsarray-0.2.5/docs/index.rst +66 -0
  5. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/_version.py +3 -3
  6. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/flag_accessor.py +13 -330
  7. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/test/test_flag_accessor.py +62 -5
  8. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/test/test_unc_accessor.py +12 -0
  9. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/unc_accessor.py +14 -13
  10. {obsarray-0.2.4 → obsarray-0.2.5/obsarray.egg-info}/PKG-INFO +5 -4
  11. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray.egg-info/requires.txt +3 -1
  12. {obsarray-0.2.4 → obsarray-0.2.5}/setup.py +14 -4
  13. {obsarray-0.2.4 → obsarray-0.2.5}/versioneer.py +2 -2
  14. obsarray-0.2.4/docs/conf.py +0 -364
  15. obsarray-0.2.4/docs/index.rst +0 -37
  16. {obsarray-0.2.4 → obsarray-0.2.5}/.pre-commit-config.yaml +0 -0
  17. {obsarray-0.2.4 → obsarray-0.2.5}/LICENSE +0 -0
  18. {obsarray-0.2.4 → obsarray-0.2.5}/MANIFEST.in +0 -0
  19. {obsarray-0.2.4 → obsarray-0.2.5}/README.md +0 -0
  20. {obsarray-0.2.4 → obsarray-0.2.5}/docs/Makefile +0 -0
  21. {obsarray-0.2.4 → obsarray-0.2.5}/docs/make.bat +0 -0
  22. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/__init__.py +0 -0
  23. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/err_corr.py +0 -0
  24. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/__init__.py +0 -0
  25. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/dataset_util.py +0 -0
  26. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/dsdoc.py +0 -0
  27. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/dstemplater.py +0 -0
  28. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/dswriter.py +0 -0
  29. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/template_util.py +0 -0
  30. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/template_util_jon.py +0 -0
  31. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/tests/__init__.py +0 -0
  32. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/tests/test_dataset_util.py +0 -0
  33. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/tests/test_dsdoc.py +0 -0
  34. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/tests/test_dswriter.py +0 -0
  35. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/templater/tests/test_template_util.py +0 -0
  36. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/test/__init__.py +0 -0
  37. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/test/test_err_corr_forms.py +0 -0
  38. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray/utils.py +0 -0
  39. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray.egg-info/SOURCES.txt +0 -0
  40. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray.egg-info/dependency_links.txt +0 -0
  41. {obsarray-0.2.4 → obsarray-0.2.5}/obsarray.egg-info/top_level.txt +0 -0
  42. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/.gitignore +0 -0
  43. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/QualityDocumentation.tex +0 -0
  44. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/CookiecutterMacros.tex +0 -0
  45. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/acronyms.tex +0 -0
  46. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/history.tex +0 -0
  47. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/metadata.tex +0 -0
  48. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/pythoninputstyle.tex +0 -0
  49. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/sil2.tex +0 -0
  50. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/sil3.tex +0 -0
  51. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/sources.bib +0 -0
  52. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/base/sources.tex +0 -0
  53. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/countpdfpages.sh +0 -0
  54. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/footnotehyper-sphinx.sty +0 -0
  55. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/obsarray_QF-59.docm +0 -0
  56. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/obsarray_requirements.docx +0 -0
  57. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/review_checklists/qf-16a.docx +0 -0
  58. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/review_checklists/qf-16b.docx +0 -0
  59. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/review_checklists/qf-16c.docx +0 -0
  60. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/review_checklists/qf-16d.docx +0 -0
  61. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/review_checklists/qf-16e.docx +0 -0
  62. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/review_checklists/qf-16f.docx +0 -0
  63. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/softwareRequirements.sty +0 -0
  64. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/sphinx.sty +0 -0
  65. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/sphinxcyrillic.sty +0 -0
  66. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/sphinxhighlight.sty +0 -0
  67. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/sphinxhowto.cls +0 -0
  68. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/sphinxmanual.cls +0 -0
  69. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/sphinxmessages.sty +0 -0
  70. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/sphinxmulticell.sty +0 -0
  71. {obsarray-0.2.4 → obsarray-0.2.5}/quality_documentation/uml/copy your design png file into this folder.txt +0 -0
  72. {obsarray-0.2.4 → obsarray-0.2.5}/setup.cfg +0 -0
  73. {obsarray-0.2.4 → obsarray-0.2.5}/tox.ini +0 -0
@@ -76,8 +76,8 @@ instance/
76
76
  .scrapy
77
77
 
78
78
  # Sphinx documentation
79
- docs/sphinx/_build/
80
- docs/sphinx/content/api
79
+ docs/_build/
80
+ docs/content/generated/
81
81
 
82
82
  # PyBuilder
83
83
  target/
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: obsarray
3
- Version: 0.2.4
4
- Summary: Measurement uncertainty handling in Python
3
+ Version: 0.2.5
4
+ Summary: Measurement data handling in Python
5
5
  Home-page: https://github.com/comet-toolkit/obsarray
6
6
  Author: CoMet Toolkit Team
7
7
  Author-email: team@comet-toolkit.org
@@ -9,9 +9,10 @@ License: GPLv3
9
9
  Platform: UNKNOWN
10
10
  Classifier: Development Status :: 2 - Pre-Alpha
11
11
  Classifier: Programming Language :: Python
12
- Classifier: Programming Language :: Python :: 3.6
13
- Classifier: Programming Language :: Python :: 3.7
14
12
  Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
15
16
  Provides-Extra: dev
16
17
  License-File: LICENSE
17
18
 
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env python
2
+ #
3
+ # obsarray documentation build configuration file, created by
4
+ # cookiecutter
5
+ #
6
+ # This file is execfile()d with the current directory set to its
7
+ # containing dir.
8
+ #
9
+ # Note that not all possible configuration values are present in this
10
+ # autogenerated file.
11
+ #
12
+ # All configuration values have a default; values that are commented out
13
+ # serve to show the default.
14
+
15
+
16
+ # If extensions (or modules to document with autodoc) are in another
17
+ # directory, add these directories to sys.path here. If the directory is
18
+ # relative to the documentation root, use os.path.abspath to make it
19
+ # absolute, like shown here.
20
+
21
+ import sphinx_autosummary_accessors
22
+ import obsarray
23
+
24
+ project_title = "obsarray".replace("_", " ").title()
25
+
26
+
27
+ # -- General configuration ---------------------------------------------
28
+
29
+ # If your documentation needs a minimal Sphinx version, state it here.
30
+ #
31
+ # needs_sphinx = '1.0'
32
+
33
+ # Attempt to make links automatially
34
+ default_role = "code"
35
+
36
+ # Add any Sphinx extension module names here, as strings. They can be
37
+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
38
+ # CFAB added napolean to support google-style docstrings
39
+ extensions = [
40
+ "sphinx.ext.autodoc",
41
+ "sphinx.ext.autosummary",
42
+ "sphinx.ext.intersphinx",
43
+ "sphinx.ext.viewcode",
44
+ "sphinx.ext.napoleon",
45
+ "IPython.sphinxext.ipython_directive",
46
+ "IPython.sphinxext.ipython_console_highlighting",
47
+ "sphinx_design",
48
+ "sphinx_autosummary_accessors",
49
+ ]
50
+
51
+ # Add any paths that contain templates here, relative to this directory.
52
+ templates_path = ["_templates", sphinx_autosummary_accessors.templates_path]
53
+
54
+ # The suffix(es) of source filenames.
55
+ # You can specify multiple suffix as a list of string:
56
+ #
57
+ # source_suffix = ['.rst', '.md']
58
+ source_suffix = ".rst"
59
+
60
+ # The master toctree document.
61
+ master_doc = "index"
62
+
63
+ # General information about the project.
64
+ project = project_title
65
+ copyright = "CoMet Toolkit Team"
66
+ author = "CoMet Toolkit Team"
67
+
68
+ # The version info for the project you're documenting, acts as replacement
69
+ # for |version| and |release|, also used in various other places throughout
70
+ # the built documents.
71
+ #
72
+ # The short X.Y version.
73
+ version = obsarray.__version__
74
+ # The full version, including alpha/beta/rc tags.
75
+ release = obsarray.__version__
76
+
77
+ # The language for content autogenerated by Sphinx. Refer to documentation
78
+ # for a list of supported languages.
79
+ #
80
+ # This is also used if you do content translation via gettext catalogs.
81
+ # Usually you set "language" from the command line for these cases.
82
+ language = None
83
+
84
+ # List of patterns, relative to source directory, that match files and
85
+ # directories to ignore when looking for source files.
86
+ # This patterns also effect to html_static_path and html_extra_path
87
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
88
+
89
+ # The name of the Pygments (syntax highlighting) style to use.
90
+ pygments_style = "sphinx"
91
+
92
+ rst_prolog = """
93
+ .. role:: python(code)
94
+ :language: python
95
+ :class: highlight
96
+ """
97
+
98
+ # If true, `todo` and `todoList` produce output, else they produce nothing.
99
+ todo_include_todos = False
100
+
101
+
102
+ # -- Options for HTML output -------------------------------------------
103
+
104
+ # The theme to use for HTML and HTML Help pages. See the documentation for
105
+ # a list of builtin themes.
106
+ #
107
+ html_theme = "sphinx_book_theme"
108
+
109
+ # Theme options are theme-specific and customize the look and feel of a
110
+ # theme further. For a list of options available for each theme, see the
111
+ # documentation.
112
+ #
113
+ # html_theme_options = {}
114
+
115
+ html_logo = "figs/comet_logo.png"
116
+
117
+ # Add any paths that contain custom static files (such as style sheets) here,
118
+ # relative to this directory. They are copied after the builtin static files,
119
+ # so a file named "default.css" will overwrite the builtin "default.css".
120
+ html_static_path = ["_static"]
121
+
122
+ # -- Options for HTMLHelp output ---------------------------------------
123
+
124
+ # Output file base name for HTML help builder.
125
+ htmlhelp_basename = "obsarraydoc"
126
+
127
+
128
+ # -- Options for LaTeX output ------------------------------------------
129
+
130
+ latex_elements = {
131
+ # The paper size ('letterpaper' or 'a4paper').
132
+ #
133
+ # 'papersize': 'letterpaper',
134
+ # The font size ('10pt', '11pt' or '12pt').
135
+ #
136
+ # 'pointsize': '10pt',
137
+ # Additional stuff for the LaTeX preamble.
138
+ #
139
+ # 'preamble': '',
140
+ # Latex figure (float) alignment
141
+ #
142
+ # 'figure_align': 'htbp',
143
+ }
144
+
145
+ # Grouping the document tree into LaTeX files. List of tuples
146
+ # (source start file, target name, title, author, documentclass
147
+ # [howto, manual, or own class]).
148
+ latex_documents = [
149
+ (
150
+ master_doc,
151
+ "user_manual.tex",
152
+ "{} Documentation".format(project_title),
153
+ "CoMet Toolkit Team",
154
+ "manual",
155
+ ),
156
+ ]
157
+
158
+
159
+ # -- Options for manual page output ------------------------------------
160
+
161
+ # One entry per manual page. List of tuples
162
+ # (source start file, name, description, authors, manual section).
163
+ man_pages = [(master_doc, "obsarray", "obsarray Documentation", [author], 1)]
164
+
165
+
166
+ # -- Options for Texinfo output ----------------------------------------
167
+
168
+ # Grouping the document tree into Texinfo files. List of tuples
169
+ # (source start file, target name, title, author,
170
+ # dir menu entry, description, category)
171
+ texinfo_documents = [
172
+ (
173
+ master_doc,
174
+ "obsarray",
175
+ "obsarray Documentation",
176
+ author,
177
+ "obsarray",
178
+ "Extension to xarray for handling uncertainty quantified observation data",
179
+ "Miscellaneous",
180
+ ),
181
+ ]
@@ -0,0 +1,66 @@
1
+ obsarray: Measurement data handling in Python
2
+ =============================================
3
+
4
+ **obsarray** is an extension to `xarray <https://docs.xarray.dev/en/stable/>`_ to support defining, storing and interfacing with measurement data - in particular, measurement uncertainty information using standardised metadata. It is designed to work well with `netCDF <https://www.unidata.ucar.edu/software/netcdf/>`_ files and for the Earth Observation community.
5
+
6
+ **obsarray** is part of the `CoMet Toolkit <https://www.comet-toolkit.org>`_ (community metrology toolkit), and can combined with the `punpy <https://punpy.readthedocs.io/en/latest/>`_ (propagating uncertainties in python) module for very simple propagation of defined data uncertainties through arbitrary python functions.
7
+
8
+ .. grid:: 2
9
+ :gutter: 2
10
+
11
+ .. grid-item-card:: Quickstart Guide
12
+ :link: content/user/quickstart
13
+ :link-type: doc
14
+
15
+ New to *obsarray*? Check out the quickstart guide for an introduction.
16
+
17
+ .. grid-item-card:: User Guide
18
+ :link: content/user/user_guide
19
+ :link-type: doc
20
+
21
+ The user guide provides a documentation and examples how to use **obsarray** to handle measurement data.
22
+
23
+ .. grid-item-card:: API Reference
24
+ :link: content/user/api
25
+ :link-type: doc
26
+
27
+ The API Reference contains a description the **obsarray** API.
28
+
29
+ .. grid-item-card:: Developer Guide
30
+ :link: content/developer/developer_guide
31
+ :link-type: doc
32
+
33
+ Guide for contributing to **obsarray** (under development).
34
+
35
+
36
+ Acknowledgements
37
+ ----------------
38
+
39
+ obsarray has been developed by `Sam Hunt <https://github.com/shunt16>`_.
40
+
41
+ The development has been funded by:
42
+
43
+ * The UK's Department for Business, Energy and Industrial Strategy's (BEIS) National Measurement System (NMS) programme
44
+ * The IDEAS-QA4EO project funded by the European Space Agency.
45
+
46
+
47
+ Project status
48
+ --------------
49
+
50
+ obsarray is under active development. It is beta software.
51
+
52
+ .. toctree::
53
+ :maxdepth: 2
54
+ :hidden:
55
+ :caption: For users
56
+
57
+ Quickstart <content/user/quickstart>
58
+ User Guide <content/user/user_guide>
59
+ API Reference <content/user/api>
60
+
61
+ .. toctree::
62
+ :maxdepth: 2
63
+ :hidden:
64
+ :caption: For developers/contributors
65
+
66
+ Developer Guide <content/developer/developer_guide>
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2023-03-06T21:45:43+0000",
11
+ "date": "2024-02-14T16:24:15+0000",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "f73bc4314bb5f5dca1e7ee619ae4c20527ed1d87",
15
- "version": "v0.2.4"
14
+ "full-revisionid": "a4c0efeceef26dd8201830bc88caab5689f25595",
15
+ "version": "v0.2.5"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -54,7 +54,7 @@ class Flag:
54
54
  """
55
55
 
56
56
  # update slice
57
- self._sli = self.expand_sli(sli)
57
+ self._sli = self._expand_sli(sli)
58
58
 
59
59
  return self
60
60
 
@@ -66,7 +66,7 @@ class Flag:
66
66
  :param flag_value: flag value as a single value boolean to apply to all data or boolean mask array
67
67
  """
68
68
 
69
- self._sli = self.expand_sli(sli)
69
+ self._sli = self._expand_sli(sli)
70
70
 
71
71
  flag_meanings, flag_masks = DatasetUtil.unpack_flag_attrs(
72
72
  self._obj[self._flag_var_name].attrs
@@ -78,11 +78,11 @@ class Flag:
78
78
  # if boolean to apply to all data
79
79
  if isinstance(flag_value, bool):
80
80
  if flag_value:
81
- self._obj[self._flag_var_name][self._sli].values[:] = (
81
+ self._obj[self._flag_var_name].values[self._sli] = (
82
82
  self._obj[self._flag_var_name][self._sli].values | flag_mask
83
83
  )
84
84
  else:
85
- self._obj[self._flag_var_name][self._sli].values[:] = (
85
+ self._obj[self._flag_var_name].values[self._sli] = (
86
86
  self._obj[self._flag_var_name][self._sli].values & ~flag_mask
87
87
  )
88
88
 
@@ -94,15 +94,15 @@ class Flag:
94
94
  i_true = np.where(flag_value == True)
95
95
  i_false = np.where(flag_value == False)
96
96
 
97
- self._obj[self._flag_var_name][self._sli].values[i_true] = (
97
+ self._obj[self._flag_var_name][self._sli].values[i_true] = np.array(
98
98
  self._obj[self._flag_var_name][self._sli].values[i_true] | flag_mask
99
99
  )
100
100
 
101
- self._obj[self._flag_var_name][self._sli].values[i_false] = (
101
+ self._obj[self._flag_var_name][self._sli].values[i_false] = np.array(
102
102
  self._obj[self._flag_var_name][self._sli].values[i_false] & ~flag_mask
103
103
  )
104
104
 
105
- def expand_sli(self, sli: Optional[tuple] = None) -> tuple:
105
+ def _expand_sli(self, sli: Optional[tuple] = None) -> tuple:
106
106
  """
107
107
  Function to expand the provided sli so that it always has the right number of dimensions
108
108
 
@@ -114,9 +114,12 @@ class Flag:
114
114
  if sli is None:
115
115
  out_sli = tuple([slice(None)] * self._obj[self._flag_var_name].ndim)
116
116
 
117
+ # Otherwise, set each : dimension to slice(None)
118
+ # E.g. if providing [0] for a variable with 3 dimensions, this becomes
119
+ # [0,slice(None),slice(None)]
117
120
  else:
118
121
  out_sli = list([slice(None)] * self._obj[self._flag_var_name].ndim)
119
- sli_list = list(sli)
122
+ sli_list = list(sli) if isinstance(sli, tuple) else [sli]
120
123
  for i in range(len(sli_list)):
121
124
  if not sli_list[i] == ":":
122
125
  out_sli[i] = sli_list[i]
@@ -268,6 +271,8 @@ class FlagVariable:
268
271
 
269
272
  return flag_meanings
270
273
 
274
+ # todo - add return set flags - array of objects, where object is list of set flags
275
+
271
276
 
272
277
  @xr.register_dataset_accessor("flag")
273
278
  class FlagAccessor(object):
@@ -435,325 +440,3 @@ class FlagAccessor(object):
435
440
 
436
441
  if __name__ == "__main__":
437
442
  pass
438
-
439
-
440
- class DatasetUtil2:
441
- @staticmethod
442
- def create_flags_variable(dim_sizes, meanings, dim_names=None, attributes=None):
443
- """
444
- Return default empty 1d xarray flag Variable
445
-
446
- :type dim_sizes: list
447
- :param dim_sizes: dimension sizes as ints, i.e. [dim1_size, dim2_size, dim3_size] (e.g. [2,3,5])
448
-
449
- :type attributes: dict
450
- :param attributes: (optional) dictionary of variable attributes, e.g. standard_name
451
-
452
- :type dim_names: list
453
- :param dim_names: (optional) dimension names as strings, i.e. ["dim1_name", "dim2_name", "dim3_size"]
454
-
455
- :return: Default empty flag vector variable
456
- :rtype: xarray.Variable
457
- """
458
-
459
- n_masks = len(meanings)
460
-
461
- data_type = DatasetUtil.return_flags_dtype(n_masks)
462
-
463
- variable = DatasetUtil.create_variable(
464
- dim_sizes,
465
- data_type,
466
- dim_names=dim_names,
467
- attributes=attributes,
468
- )
469
-
470
- #initialise flags to zero (instead of fillvalue)
471
- variable.values=0*variable.values
472
-
473
- # add flag attributes
474
- variable.attrs["flag_meanings"] = (
475
- str(meanings)[1:-1].replace("'", "").replace(",", "")
476
- )
477
- variable.attrs["flag_masks"] = str([2 ** i for i in range(0, n_masks)])[1:-1]
478
-
479
- # todo - make sure flags can't have units
480
-
481
- return variable
482
-
483
- @staticmethod
484
- def return_flags_dtype(n_masks):
485
- """
486
- Return required flags array data type
487
-
488
- :type n_masks: int
489
- :param n_masks: number of masks required in flag array
490
-
491
- :return: data type
492
- :rtype: dtype
493
- """
494
-
495
- if n_masks <= 8:
496
- return np.int8
497
- elif n_masks <= 16:
498
- return np.int16
499
- elif n_masks <= 32:
500
- return np.int32
501
- else:
502
- return np.int64
503
-
504
- @staticmethod
505
- def add_encoding(
506
- variable, dtype, scale_factor=1.0, offset=0.0, fill_value=None, chunksizes=None
507
- ):
508
- """
509
- Add encoding to xarray Variable to apply when writing netCDF files
510
-
511
- :type variable: xarray.Variable
512
- :param variable: data variable
513
-
514
- :type dtype: type
515
- :param dtype: numpy data type
516
-
517
- :type scale_factor: float
518
- :param scale_factor: variable scale factor
519
-
520
- :type offset: float
521
- :param offset: variable offset value
522
-
523
- :type fill_value: int/float
524
- :param fill_value: (optional) fill value
525
-
526
- :type chunksizes: float
527
- :param chunksizes: (optional) chucksizes
528
- """
529
-
530
- # todo - make sure flags can't have encoding added
531
-
532
- encoding_dict = {
533
- "dtype": dtype,
534
- "scale_factor": scale_factor,
535
- "add_offset": offset,
536
- }
537
-
538
- if chunksizes is not None:
539
- encoding_dict.update({"chunksizes": chunksizes})
540
-
541
- if fill_value is not None:
542
- encoding_dict.update({"_FillValue": fill_value})
543
-
544
- variable.encoding = encoding_dict
545
-
546
- @staticmethod
547
- def get_default_fill_value(dtype):
548
- """
549
- Returns default fill_value for given data type
550
-
551
- :type dtype: type
552
- :param dtype: numpy dtype
553
-
554
- :return: CF-conforming fill value
555
- :rtype: fill_value
556
- """
557
-
558
- if dtype == np.int8:
559
- return np.int8(-129)
560
- if dtype == np.uint8:
561
- return np.uint8(-1)
562
- elif dtype == np.int16:
563
- return np.int16(-32769)
564
- elif dtype == np.uint16:
565
- return np.uint16(-1)
566
- elif dtype == np.int32:
567
- return np.int32(-2147483649)
568
- elif dtype == np.uint32:
569
- return np.uint32(-1)
570
- elif dtype == np.int64:
571
- return np.int64(-9223372036854775808)
572
- elif dtype == np.float32:
573
- return np.float32(9.96921e36)
574
- elif dtype == np.float64:
575
- return np.float64(9.969209968386869e36)
576
-
577
- @staticmethod
578
- def _get_flag_encoding(da):
579
- """
580
- Returns flag encoding for flag type data array
581
- :type da: xarray.DataArray
582
- :param da: data array
583
- :return: flag meanings
584
- :rtype: list
585
- :return: flag masks
586
- :rtype: list
587
- """
588
-
589
- try:
590
- flag_meanings = da.attrs["flag_meanings"].split()
591
- flag_masks = [int(fm) for fm in da.attrs["flag_masks"].split(",")]
592
- except KeyError:
593
- raise KeyError(da.name + " not a flag variable")
594
-
595
- return flag_meanings, flag_masks
596
-
597
- @staticmethod
598
- def unpack_flags(da):
599
- """
600
- Breaks down flag data array into dataset of boolean masks for each flag
601
- :type da: xarray.DataArray
602
- :param da: dataset
603
- :return: flag masks
604
- :rtype: xarray.Dataset
605
- """
606
-
607
- flag_meanings, flag_masks = DatasetUtil._get_flag_encoding(da)
608
-
609
- ds = Dataset()
610
- for flag_meaning, flag_mask in zip(flag_meanings, flag_masks):
611
- ds[flag_meaning] = DatasetUtil.create_variable(
612
- list(da.shape), bool, dim_names=list(da.dims)
613
- )
614
- ds[flag_meaning] = (da & flag_mask).astype(bool)
615
-
616
- return ds
617
-
618
- @staticmethod
619
- def get_flags_mask_or(da, flags=None):
620
- """
621
- Returns boolean mask for set of flags, defined as logical or of flags
622
-
623
- :type da: xarray.DataArray
624
- :param da: dataset
625
-
626
- :type flags: list
627
- :param flags: list of flags (if unset all data flags selected)
628
-
629
- :return: flag masks
630
- :rtype: numpy.ndarray
631
- """
632
-
633
- flags_ds = DatasetUtil.unpack_flags(da)
634
-
635
- flags = flags if flags is not None else flags_ds.variables
636
- mask_flags = [flags_ds[flag].values for flag in flags]
637
-
638
- return np.logical_or.reduce(mask_flags)
639
-
640
- @staticmethod
641
- def get_flags_mask_and(da, flags=None):
642
- """
643
- Returns boolean mask for set of flags, defined as logical and of flags
644
-
645
- :type da: xarray.DataArray
646
- :param da: dataset
647
-
648
- :type flags: list
649
- :param flags: list of flags (if unset all data flags selected)
650
-
651
- :return: flag masks
652
- :rtype: numpy.ndarray
653
- """
654
-
655
- flags_ds = DatasetUtil.unpack_flags(da)
656
-
657
- flags = flags if flags is not None else flags_ds.variables
658
- mask_flags = [flags_ds[flag].values for flag in flags]
659
-
660
- return np.logical_and.reduce(mask_flags)
661
-
662
- @staticmethod
663
- def set_flag(da, flag_name, error_if_set=False):
664
- """
665
- Sets named flag for elements in data array
666
- :type da: xarray.DataArray
667
- :param da: dataset
668
- :type flag_name: str
669
- :param flag_name: name of flag to set
670
- :type error_if_set: bool
671
- :param error_if_set: raises error if chosen flag is already set for any element
672
- """
673
-
674
- set_flags = DatasetUtil.unpack_flags(da)[flag_name]
675
-
676
- if np.any(set_flags == True) and error_if_set:
677
- raise ValueError(
678
- "Flag " + flag_name + " already set for variable " + da.name
679
- )
680
-
681
- # Find flag mask
682
- flag_meanings, flag_masks = DatasetUtil._get_flag_encoding(da)
683
- flag_bit = flag_meanings.index(flag_name)
684
- flag_mask = flag_masks[flag_bit]
685
-
686
- da.values = da.values | flag_mask
687
-
688
- return da
689
-
690
- @staticmethod
691
- def unset_flag(da, flag_name, error_if_unset=False):
692
- """
693
- Unsets named flag for specified index of dataset variable
694
- :type da: xarray.DataArray
695
- :param da: data array
696
- :type flag_name: str
697
- :param flag_name: name of flag to unset
698
- :type error_if_unset: bool
699
- :param error_if_unset: raises error if chosen flag is already set at specified index
700
- """
701
-
702
- set_flags = DatasetUtil.unpack_flags(da)[flag_name]
703
-
704
- if np.any(set_flags == False) and error_if_unset:
705
- raise ValueError(
706
- "Flag " + flag_name + " already set for variable " + da.name
707
- )
708
-
709
- # Find flag mask
710
- flag_meanings, flag_masks = DatasetUtil._get_flag_encoding(da)
711
- flag_bit = flag_meanings.index(flag_name)
712
- flag_mask = flag_masks[flag_bit]
713
-
714
- da.values = da.values & ~flag_mask
715
-
716
- return da
717
-
718
- @staticmethod
719
- def get_set_flags(da):
720
- """
721
- Return list of set flags for single element data array
722
- :type da: xarray.DataArray
723
- :param da: single element data array
724
- :return: set flags
725
- :rtype: list
726
- """
727
-
728
- if da.shape != ():
729
- raise ValueError("Must pass single element data array")
730
-
731
- flag_meanings, flag_masks = DatasetUtil._get_flag_encoding(da)
732
-
733
- set_flags = []
734
- for flag_meaning, flag_mask in zip(flag_meanings, flag_masks):
735
- if da & flag_mask:
736
- set_flags.append(flag_meaning)
737
-
738
- return set_flags
739
-
740
- @staticmethod
741
- def check_flag_set(da, flag_name):
742
- """
743
- Returns if flag for single element data array
744
- :type da: xarray.DataArray
745
- :param da: single element data array
746
- :type flag_name: str
747
- :param flag_name: name of flag to set
748
- :return: set flags
749
- :rtype: list
750
- """
751
-
752
- if da.shape != ():
753
- raise ValueError("Must pass single element data array")
754
-
755
- set_flags = DatasetUtil.get_set_flags(da)
756
-
757
- if flag_name in set_flags:
758
- return True
759
- return False