Skip to content

Commit 8b099cb

Browse files
committed
Initial implementation
0 parents  commit 8b099cb

9 files changed

+502
-0
lines changed

.gitignore

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Created by .ignore support plugin (hsz.mobi)
2+
### JetBrains template
3+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
4+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5+
.idea
6+
7+
## Plugin-specific files:
8+
9+
# IntelliJ
10+
/out/
11+
12+
# mpeltonen/sbt-idea plugin
13+
.idea_modules/
14+
15+
# JIRA plugin
16+
atlassian-ide-plugin.xml
17+
18+
# Crashlytics plugin (for Android Studio and IntelliJ)
19+
com_crashlytics_export_strings.xml
20+
crashlytics.properties
21+
crashlytics-build.properties
22+
fabric.properties
23+
### Vim template
24+
# swap
25+
[._]*.s[a-w][a-z]
26+
[._]s[a-w][a-z]
27+
# session
28+
Session.vim
29+
# temporary
30+
.netrwhist
31+
*~
32+
# auto-generated tag files
33+
tags
34+
### Python template
35+
# Byte-compiled / optimized / DLL files
36+
__pycache__/
37+
*.py[cod]
38+
*$py.class
39+
40+
# C extensions
41+
*.so
42+
43+
# Distribution / packaging
44+
.Python
45+
env/
46+
build/
47+
develop-eggs/
48+
dist/
49+
downloads/
50+
eggs/
51+
.eggs/
52+
lib/
53+
lib64/
54+
parts/
55+
sdist/
56+
var/
57+
*.egg-info/
58+
.installed.cfg
59+
*.egg
60+
61+
# PyInstaller
62+
# Usually these files are written by a python script from a template
63+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
64+
*.manifest
65+
*.spec
66+
67+
# Installer logs
68+
pip-log.txt
69+
pip-delete-this-directory.txt
70+
71+
# Unit test / coverage reports
72+
htmlcov/
73+
.tox/
74+
.coverage
75+
.coverage.*
76+
.cache
77+
nosetests.xml
78+
coverage.xml
79+
*,cover
80+
.hypothesis/
81+
82+
# Translations
83+
*.mo
84+
*.pot
85+
86+
# Django stuff:
87+
*.log
88+
local_settings.py
89+
90+
# Flask stuff:
91+
instance/
92+
.webassets-cache
93+
94+
# Scrapy stuff:
95+
.scrapy
96+
97+
# Sphinx documentation
98+
docs/_build/
99+
100+
# PyBuilder
101+
target/
102+
103+
# Jupyter Notebook
104+
.ipynb_checkpoints
105+
106+
# pyenv
107+
.python-version
108+
109+
# celery beat schedule file
110+
celerybeat-schedule
111+
112+
# dotenv
113+
.env
114+
115+
# virtualenv
116+
.venv/
117+
venv/
118+
ENV/
119+
120+
# Spyder project settings
121+
.spyderproject
122+
123+
# Rope project settings
124+
.ropeproject
125+

CHANGELOG.md

Whitespace-only changes.

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Stefan Codrescu
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# NetCDF Flag Wrapper (ncflag)
2+
3+
So... you want to inspect CF compliant NetCDF flag Variables?
4+
5+
6+
## TL;DR
7+
8+
Install the utility with with pip:
9+
```
10+
pip install ncflag
11+
```
12+
13+
On the command line, use `ncflag`:
14+
15+
```
16+
Usage: ncflag [OPTIONS] NCFILE FLAG
17+
18+
Options:
19+
-v, --version Show the version and exit.
20+
--show_flags PATH Print the flags this tool can inspect.
21+
--use_time_var TEXT
22+
-l [DEBUG|INFO|WARNING|ERROR|CRITICAL]
23+
log level
24+
--help Show this message and exit.
25+
26+
```
27+
28+
Notes:
29+
30+
Use --show_flags to discover what flags can be inspected.
31+
32+
Limitation: can only inspect flags of at most one dimension. See details below for dealing with multidimensional flags.
33+
34+
35+
The nominal output with --use_time_var specified is shown below. Without use_time_var, the index along the
36+
dimension will be printed instead of a iso 8601 timestamp.
37+
```text
38+
2017-11-27T21:07:41.543778: [u'data_quality_error']
39+
2017-11-27T21:07:42.543812: [u'good_data']
40+
2017-11-27T21:07:43.543807: [u'good_data']
41+
2017-11-27T21:07:44.543802: [u'good_data']
42+
```
43+
44+
## Multidimensional Flags
45+
46+
Occasionally, by some poor misfortune, you may encounter multidimensional flag variables. These are currently not
47+
supported by the Command Line Interface (CLI), however, the FlagWrap class can still be used in code, or through an
48+
interactive (IPython) session. The `FlagWrap.get_flags_set_at_index` can be passed a tuple to get the flags set in
49+
a multidimensional flag variable. Below is an example.
50+
51+
```python
52+
from ncflag import FlagWrap
53+
import netCDF4 as nc
54+
55+
with nc.Dataset("somenetcdf.nc") as nc_in:
56+
v = nc_in.variables["mutidim_variable"]
57+
print(v.shape) # --> (2, 10)
58+
w = FlagWrap(v)
59+
print(w.get_flags_set_at_index((0, 0))) # --> ["good_quality_qf"]
60+
```
61+
62+
## Testing
63+
64+
TODO!
65+
66+
## Development
67+
68+
Setting up a virtualenv is recommended for development.
69+
70+
```
71+
virtualenv venv
72+
. venv/bin/activate
73+
pip install --editable .
74+
```
75+
76+
---------------------
77+
78+
Deploy to pip, after testing with python2 and python3:
79+
80+
```bash
81+
rm -r dist/
82+
python setup.py bdist_wheel --universal
83+
twine upload dist/*
84+
```
85+

ncflag/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from flag_wrapper import FlagWrap

ncflag/cli.py

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import click
2+
import logging
3+
import pkg_resources
4+
import netCDF4 as nc
5+
from flag_wrapper import FlagWrap
6+
7+
try:
8+
version = pkg_resources.require("ncflag")[0].version
9+
except pkg_resources.DistributionNotFound:
10+
version = "unknown"
11+
12+
def show_flags(ctx, param, ncfile):
13+
if not ncfile or ctx.resilient_parsing:
14+
return
15+
valid_flags = []
16+
with nc.Dataset(ncfile) as nc_in: # type: nc.Dataset
17+
for k, v in nc_in.variables.items():
18+
if hasattr(v, "flag_values") and hasattr(v, "flag_meanings") and len(v.dimensions) == 1:
19+
valid_flags.append(k)
20+
click.echo("Inspectable flags: %s" % map(str, valid_flags))
21+
ctx.exit()
22+
23+
24+
@click.command()
25+
@click.version_option(version, "-v", "--version")
26+
@click.option("--show_flags", callback=show_flags, expose_value=False, is_eager=True,
27+
type=click.Path(exists=True, dir_okay=False), help="Print the flags this tool can inspect.")
28+
@click.argument("ncfile", type=click.Path(exists=True, dir_okay=False))
29+
@click.argument("flag", type=click.STRING)
30+
@click.option("--use_time_var", type=click.STRING, default=None)
31+
@click.option("-l", help="log level", type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']),
32+
default="WARNING")
33+
def cli(ncfile, flag, use_time_var, l):
34+
logging.getLogger().setLevel(l)
35+
with nc.Dataset(ncfile) as nc_in: # type: nc.Dataset
36+
# initial checks
37+
v = nc_in.variables[flag] # type: nc.Variable
38+
assert hasattr(v, "flag_values"), "%s is not CF compliant flag, missing flag_values" % flag
39+
assert hasattr(v, "flag_meanings"), "%s is not CF compliant flag, missing flag_meanings" % flag
40+
assert len(v.dimensions) == 1, "multidimensional flags are not supported, see docs and use ipython instead"
41+
w = FlagWrap(v)
42+
if use_time_var is not None:
43+
t = nc_in.variables[use_time_var] # type: nc.Variable
44+
assert t.dimensions == v.dimensions, "To print flags by time, time flag must share dimensions"
45+
assert hasattr(t, "units"), "did not find units on time flag %s" % use_time_var
46+
for i, dt in enumerate(nc.num2date(t[:], t.units)):
47+
out_time = dt.isoformat() if dt is not None else "__________________________"
48+
click.echo("%s: %s" % (out_time, w.get_flags_set_at_index(i)))
49+
else:
50+
for i in range(v.size):
51+
click.echo("%s: %s" % (i, w.get_flags_set_at_index(i)))
52+
53+
54+
if __name__ == "__main__":
55+
56+
console = logging.StreamHandler()
57+
logging.getLogger().addHandler(console)
58+
59+
cli()

0 commit comments

Comments
 (0)