diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index ecc4c31b..64d43174 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -25,16 +25,15 @@ jobs: restore-keys: | mkdocs-material- - run: | - python -m pip install --upgrade pip - pip install ".[dev]" - pip install -r docs-requirements.txt + pip install poetry + poetry install --with docs,geo - run: | git config --global user.name "${GITHUB_ACTOR}" git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" - run: | if [[ "${{github.ref_name}}" == "main" ]]; then - mike deploy main --push --force + poetry run mike deploy main --push --force else - mike deploy dev --push --force + poetry run mike deploy dev --push --force fi - mike set-default dev --push + poetry run mike set-default dev --push diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index 0b202b9b..5dc9021d 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -22,11 +22,10 @@ jobs: restore-keys: | mkdocs-material- - run: | - python -m pip install --upgrade pip - pip install -r docs-requirements.txt - pip install ".[dev]" + pip install poetry + poetry install --with docs,geo - run: | git config --global user.name "${GITHUB_ACTOR}" git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" - run: | - mike deploy test + poetry run mkdocs build -s -d test \ No newline at end of file diff --git a/.github/workflows/test-package.yaml b/.github/workflows/test-package.yaml new file mode 100644 index 00000000..db76d3e0 --- /dev/null +++ b/.github/workflows/test-package.yaml @@ -0,0 +1,68 @@ +name: Test package for linter issues and run tests + +on: + pull_request: + branches: + - main + - development + paths: + - .github/workflows/test-package.yaml + - setup.py + - MANIFEST.in + - ocean_data_parser/** + - tests/** + + push: + branches: + - main + - development + paths: + - setup.py + - MANIFEST.in + - ocean_data_parser/** + - tests/** + +jobs: + testing: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry install --with dev,geo + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + poetry run flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + poetry run flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Run ruff + run: poetry run ruff check --output-format=github . + - name: Review if metadata is updated + uses: dorny/paths-filter@v3 + id: changes + with: + filters: | + vocabularies: + - 'ocean_data_parser/vocabularies/**' + - 'ocean_data_parser/metatadata/**' + - 'tests/test_metadata.py' + changelog: + - 'CHANGELOG.md' + - name: Run tests with metadata tests + if: steps.changes.outputs.vocabularies == 'true' + run: poetry run pytest -W error::UserWarning --nerc-vocab -n auto + - name: Run tests without metadata tests + if: steps.changes.outputs.vocabularies == 'false' + run: poetry run pytest -W error::UserWarning -k "not test_metadata" -n auto + - name: Run benchmark + run: poetry run pytest tests/run_benchmark.py --benchmark-json output.json + - name: Update CHANGELOG + if: steps.changes.outputs.changelog == 'false' + run: exit 1 + \ No newline at end of file diff --git a/.github/workflows/test-pytest.yaml b/.github/workflows/test-pytest.yaml deleted file mode 100644 index fb44e283..00000000 --- a/.github/workflows/test-pytest.yaml +++ /dev/null @@ -1,45 +0,0 @@ -name: Test linter and full pytest - -on: - pull_request: - branches: - - main - - development - paths: - - setup.py - - MANIFEST.in - - ocean_data_parser/** - - tests/** - - push: - branches: - - main - - development - paths: - - setup.py - - MANIFEST.in - - ocean_data_parser/** - - tests/** - -jobs: - testing: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install ".[dev]" - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with pytest parsers - run: pytest -W error::UserWarning - - name: Run benchmark - run: pytest tests/run_benchmark.py --benchmark-json output.json \ No newline at end of file diff --git a/.gitignore b/.gitignore index 61f6557f..8770e48b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,9 +11,6 @@ dist/ /pycurrents .DS_Store -.vscode/launch.json - -.vscode/settings.json /**/local_tests/* /tests/**/*_test.nc pdc-amundsen-conversion.log @@ -45,3 +42,6 @@ docs/user_guide/vocabularies/*.md docs/user_guide/parsers/parser-list.md site/** +.python-version + +.vscode/launch.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7f4ed671 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.pytestEnabled": true, + "python.testing.unittestEnabled": false, +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..36cbe47e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## `0.5.0` - 2024-06-20 + +### Added + +- Test get_path_generation_input on all test files and parsers +- Run all tests in parallel with pytest-xdist +- Add --parser-kwargs option to odpy convert command line interface to pass +inputs to parser + +### Fixed + +- Fix star_oddi parser time variable to output a datetime dataarray. +- Fix onset parser date time handling. +- Avoid reimporting parser if already imported in read.file with parser +defined via string expression. +- Fix Amundsen int timestamp format +- Drop trip_tag attribute from dfo.nafc.pcnv parser +- Make dfo.nafc.pcnv parser attempt to retrieve metqa file info by default. + +## `v0.4.0` - 2024-05-04 + +### Added + +- Test platforms vocabulary items and match them to NERC C17 vocabulary. + +### Changed + +- Refactor Seabird Parser + - Handle better processing related attributes + - On-platform processing + - SBE Data Processing modules + - Post processing (NAFC pcnv Format `* QA Applied:`) + - Retrieve more of the information available stored within Seabird header as attributes + - Structure history +- Upgrade odf.bio reference NetCDF to reflect the changes. +- Upgrate dfo platforms for the odf and nafc parsers. + +### Removed + +- Historical ODF platform vocabulary which wasn't used anywhere. \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 5dc6b429..4d773178 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -658,7 +658,7 @@ notice like this when it starts in an interactive mode: This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. -The hypothetical commands `show w' and `show c' should show the appropriate +The hypothetical commands `show w' and`show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 7f647d34..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -recursive-include ocean_data_parser/vocabularies * -include ocean_data_parser/parsers/dfo/odf_source/*config.json -recursive-include ocean_data_parser/parsers/dfo/odf_source/references * -recursive-include ocean_data_parser/parsers/dfo/odf_source/references/geographical_areas * -include ocean_data_parser/batch/default-batch-config.yaml \ No newline at end of file diff --git a/README.md b/README.md index 87dcc050..57dc9f5c 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,17 @@ - ![Logo](docs/images/logo_EN_FR-1024x208.png#gh-light-mode-only) ![Logo](docs/images/cioos-national_EN_FR_W-01.png#gh-dark-mode-only) - - # ocean-data-parser [![Build documentation](https://github.com/cioos-siooc/ocean-data-parser/actions/workflows/deploy-docs.yaml/badge.svg)](https://github.com/cioos-siooc/ocean-data-parser/actions/workflows/deploy-docs.yaml) - `ocean-data-parser` - a Python package for parsing oceanographic proprietary data formats to [xarray Dataset](https://docs.xarray.dev/en/stable/). Documentation [here](https://cioos-siooc.github.io/ocean-data-parser/). ## Installation + Install the package with the following command, ideally within a virtual environment: ```console @@ -26,32 +19,40 @@ pip install git+https://github.com/cioos-siooc/ocean-data-parser.git ``` ### How to + #### odpy cli + Once installed, the package is usable via the command line interface: + ```console odpy --help ``` To batch convert a series of files to NetCDF: + ``` odpy convert -i '**/*.cnv' -p 'seabird.cnv' ``` + #### Format auto-detection + Load a compatible file with the automated parser detection method: ```python -import ocean_data_parser.parsers +import ocean_data_parser.read # Load a file to an xarray object -ds = ocean_data_parser.parsers.file('Path to file') +ds = ocean_data_parser.read.file('Path to file') # Save to netcdf ds.to_netcdf('save-path.nc') ``` + !!!warning The parser detection method relies on the file extension and the first few lines present within the given file. Or specify the specific parser to use for this file format: + ``` python from ocean_data_parser.parsers import seabird @@ -61,10 +62,11 @@ ds = seabird.cnv('Path to seabird cnv file') # Save to netcdf ds.to_netcdf('save-path.nc') ``` + The `ocean-data-parser` can then be used within either a Python package, script or jupyter notebook. See the [documentation Notebook section](https://cioos-siooc.github.io/ocean-data-parser) for examples of how to use the package within a jupyter notebook. ## Contributions -All contributions are welcome! +All contributions are welcome! -Please create a new [discussion](https://github.com/cioos-siooc/ocean-data-parser/discussions) or [issue](https://github.com/cioos-siooc/ocean-data-parser/issues) within the GitHub repository for any questions, ideas and suggestions. +Please create a new [discussion](https://github.com/cioos-siooc/ocean-data-parser/discussions) or [issue](https://github.com/cioos-siooc/ocean-data-parser/issues) within the GitHub repository for any questions, ideas and suggestions. diff --git a/docs-requirements.txt b/docs-requirements.txt deleted file mode 100644 index 53eb71e6..00000000 --- a/docs-requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -mkdocs -mkdocs-click -mkdocs-gen-files -mkdocs-jupyter -mkdocs-material -mkdocs-simple-hooks -mkdocstrings[python] -mkdocs-section-index -mike -tabulate -pandas -material-plausible-plugin \ No newline at end of file diff --git a/docs/get_started/development.md b/docs/development.md similarity index 61% rename from docs/get_started/development.md rename to docs/development.md index 551035e9..56b80cbf 100644 --- a/docs/get_started/development.md +++ b/docs/development.md @@ -1,45 +1,68 @@ +# Development + +This page contains all the information necessary to setup a local development environment. + +## :octicons-person-add-24: Contribution + +All contributions are welcome! + +Please create a new [discussion](https://github.com/cioos-siooc/ocean-data-parser/discussions) +or [issue](https://github.com/cioos-siooc/ocean-data-parser/issues) within the +github repository for any questions, ideas and suggestions. + +## :octicons-download-24: Installation -### Installation Clone the project locally -```shell +```console git clone git+https://github.com/cioos-siooc/ocean-data-parser.git ``` -Go to the project directory +We recommend using [poetry](https://python-poetry.org/) to manage the environment and [pyenv](https://github.com/pyenv/pyenv) to manage the python version: -```shell - cd ocean-data-parser +```console +pyenv install 3.11.2 +pyenv shell 3.11.2 +``` + +Install poetry python environment with: + +```console +pip install poetry ``` -Install dependencies +Go to the project directory and install dependancies in a poetry environment. -```shell - pip install -e ".[dev]" +```console + cd ocean-data-parser + poetry install --with dev ``` -### Testing +## :material-test-tube: Testing + The package use pytest to run a series of tests in order to help the development of the different aspect of the package. Within a developping environment, to run the different tests, run the pytest commmand through your terminal within the base directory of the repository. Pytest can also be integrated with different IDE and is run on any pushes and PR to the `main` and `development` branches. -### Parsers Tests +## :material-checkbox-multiple-marked-circle-outline: Parsers Tests + The package contains a number of different parsers compatible with different standard file formats. Each parser is tested on a series of test files made available within the [test file directory](https://github.com/cioos-siooc/ocean-data-parser/blob/main/tests/parsers_test_files) The different tests executed on each individual parsers can be subdivided in 3 main categories: + 1. Parse test file to an xarray dataset 2. Parse test file to an xarray dataset and save to a NetCDF4 file. 3. Parse test file to an xarray dataset and compare to a reference file ('*_reference.nc) if made available. Any differences are flagged 4. *(in development)* Assess parsed xarray object compliance with the different convention by using the ioos-compliance checker, resulting objects should be to a minimum compliante to ACDD 1.3 and CF 1.6. Other conventions can be added by adding them to the xarray object global attribute `Convention`. +## :octicons-book-24: Documentation Build -### Documentation To run a local instance of the documentation webpage. Install the dependancies: + ```console -pip install -r docs-requirements.txt +poetry install --group docs ``` And run the command: -```shell -mike serve +```console +poetry run mkdocs serve ``` Any documentation changes to the main and development branches will automatically update respectively the main and dev document pages. - diff --git a/docs/get_started/contributions.md b/docs/get_started/contributions.md deleted file mode 100644 index 41fdb228..00000000 --- a/docs/get_started/contributions.md +++ /dev/null @@ -1,3 +0,0 @@ -All contributions are welcome! - -Please create a new [discussion](https://github.com/cioos-siooc/ocean-data-parser/discussions) or [issue](https://github.com/cioos-siooc/ocean-data-parser/issues) within the github repository for any questions, ideas and suggestions. \ No newline at end of file diff --git a/docs/get_started/how-to.md b/docs/get_started/how-to.md deleted file mode 100644 index 02fee174..00000000 --- a/docs/get_started/how-to.md +++ /dev/null @@ -1,56 +0,0 @@ -### Conversion - -??? question "Convert files to netcdf" - - You can use the odpy command within a terminal to access the different functions of the `ocean-data-parser`. - - Convert all cnv files in subdiretories by using the seabird.cnv parser and save to output directory: - ```console - odpy convert --input-path input/**/*.cnv --parser=seabird.cnv --output_path=output - ``` - - See [commmand line section](../user_guide/odpy#convert.md) for more detail or use the command: `odpy convert --help` - -??? question "Avoid reconverting over again the same files" - - The `ocean-data-parser` provides a file retristry which can be used to: - - track which files were converted and outputted where - - error associated with each files - - file modified time - - file hash - - If activated, a registry file (*.csv/*.parquet) will be saved and any time a conversion is rerun. odpy will first compare the available files to the already parsed files available within the registry and will only convert the ones which have changes. Those changes are primarily based on the file modified time, each modified file is then rehashed and if that hash is different the file will be reconverted and the output overwritten (default) - -### Parser handling - -??? question "Load any compatible files in my own project" - - - To load a compatible file you can use the automated parser detection method: - - ```python - from ocean_data_parser import read - - # Load a file to an xarray object - ds = read.file('Path to file') - - # Save to netcdf - ds.to_netcdf('save-path.nc') - ``` - - :warning: The parser detection method relies on the file extension and the first few lines present within the given file. It is preferable to define a specific parser when a tool is used in production. - - - -??? question "Load a file with a specific parser in my own project" - You can import a specific parser via the `ocean_data_parser.parser` - ``` python - from ocean_data_parser.parsers import seabird - - # Load a seabird cnv file as an xarray dataset - ds = seabird.cnv('Path to seabird cnv file') - - # Save to netcdf - ds.to_netcdf('save-path.nc') - ``` - The `ocean-data-parser` can then be used within either a python package, script or jupyter notebook. See [documentation Notebook section](https://cioos-siooc.github.io/ocean-data-parser) for examples on how to use the package within a jupyter notebook. \ No newline at end of file diff --git a/docs/get_started/installation.md b/docs/get_started/installation.md deleted file mode 100644 index efba4a45..00000000 --- a/docs/get_started/installation.md +++ /dev/null @@ -1,13 +0,0 @@ -Install the package with the following command, ideally within a virtual environment: - -```console -pip install git+https://github.com/cioos-siooc/ocean-data-parser.git -``` - -Once installed, you can test the package through the command line with: - -```console -odpy --help -``` -!!! Info - For more details on how to use the command line interface see [commmand line section](../user_guide/odpy.md). \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 78bff523..8ae4ba23 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,63 @@ --- hide: - navigation -title: Home + - title +title: Ocean Data Parser Documentation template: home.html --- + +# + +## :octicons-download-24: Installation + +Install the package in a local environment via the following command: + +```console +pip install git+https://github.com/cioos-siooc/ocean-data-parser.git +``` + +## How to + +### [:octicons-command-palette-24: via Command Line Interface `odpy`](user_guide/cli.md) + +Once installed, the package is usable via the command line interface +via the `odpy` command. As an example to convert a series of cnv files to netcdf, +you can use the following command: + +```console +odpy convert -i '**/*.cnv' +``` + +For futher details see [here](user_guide/cli.md) or run the following command: + +```console +odpy --help +``` + +### [:material-file-find: via `ocean_data_parser.read.file`](user_guide/read.md) + +Load a compatible file with the global read.file method + +```py title="from ocean_data_parser import read" +from ocean_data_parser import read + +# Load a file to an xarray object +ds = read.file('Path to file') + +# Save to netcdf +ds.to_netcdf('save-path.nc') +``` + +### [:material-sitemap-outline: via `from ocean_data_parser.parsers import ...`](user_guide/parsers/index.md) + +Or specify the specific parser to use for this file format: + +```py title="from ocean_data_parser.parsers import ..." +from ocean_data_parser.parsers import seabird + +# Load a seabird cnv file as an xarray dataset +ds = seabird.cnv('Path to seabird cnv file') + +# Save to netcdf +ds.to_netcdf('save-path.nc') +``` diff --git a/docs/release-notes.md b/docs/release-notes.md new file mode 100644 index 00000000..90cb31c6 --- /dev/null +++ b/docs/release-notes.md @@ -0,0 +1 @@ +--8<-- "CHANGELOG.md" \ No newline at end of file diff --git a/docs/scripts/hooks.py b/docs/scripts/hooks.py index 4d232113..7f30a3dc 100644 --- a/docs/scripts/hooks.py +++ b/docs/scripts/hooks.py @@ -91,19 +91,30 @@ def copy_notebooks(output="docs/notebooks"): shutil.copy(notebook, docs_notebooks / notebook.name) -def get_parser_list(output="docs/user_guide/parsers/parser-list.md"): +def get_parser_list(output="docs/user_guide/parsers/index.md"): def _get_parser_page_link(parser): if "." not in parser: - return parser + return parser, f"[{parser}]({parser.replace('_','-')}.md)" parser_module, _ = parser.rsplit(".", 1) - return f"[{parser}](parsers/{parser_module.replace('.','/')}/#ocean_data_parser.parsers.{parser})" - - with open(output, "w") as file: - file.write("## Available Parsers\n") - file.write( - "\n".join([f"- {_get_parser_page_link(parser)}" for parser in PARSERS]) + return ( + parser_module, + f"[{parser}]({parser_module.replace('.','/').replace('_','-')}.md#ocean_data_parser.parsers.{parser})", ) + index_html = Path("docs/user_guide/parsers/header-index.md").read_text() + table_parser = {} + for parser in PARSERS: + parser_module, link = _get_parser_page_link(parser) + if parser_module not in table_parser: + table_parser[parser_module] = [] + table_parser[parser_module].append(link) + parsers_toc = "" + for parser_module in sorted(table_parser): + parsers_toc += f"[{parser_module.upper()}]({parser_module.replace('.','/').replace('_','-')}.md)\n\n- " + parsers_toc += "\n- ".join(table_parser[parser_module]) + "\n\n" + index_html = index_html.replace("{{ parsers_list }}", parsers_toc) + Path(output).write_text(index_html) + def on_pre_build(config, **kwargs) -> None: add_vocabularies_dir() diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index d5c3c6c0..3e21cc4e 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -13,8 +13,8 @@ th, td { } .md-typeset__table table:not([class]) thead { - background-color: var(--md-primary-fg-color--light); - /* color: var(--md-primary-bg-color) */ + background-color: var(--md-primary-fg-color--dark); + color: var(--md-primary-bg-color) } .md-typeset__table { @@ -33,12 +33,12 @@ th, td { /* light mode alternating table bg colors */ .md-typeset__table tr:nth-child(2n) { - background-color: #f8f8f8; + background-color: #83838325; } /* dark mode alternating table bg colors */ [data-md-color-scheme="slate"] .md-typeset__table tr:nth-child(2n) { - background-color: hsla(var(--md-hue),25%,25%,1) + background-color: #83838325; } .md-footer { diff --git a/docs/theme_override_home/home.html b/docs/theme_override_home/home.html index 52ab7f48..3380e719 100644 --- a/docs/theme_override_home/home.html +++ b/docs/theme_override_home/home.html @@ -16,10 +16,12 @@ display: flex; height: 100%; } - + .main-button{ + width: 180px; + } .tx-container { padding-top: .0rem; - background: linear-gradient(to bottom, var(--md-primary-fg-color),hsla(160deg,47%,55%,1) 100%) + background: linear-gradient(to bottom, var(--md-primary-fg-color),rgba(86, 194, 158, 0.8) 100%) } .tx-hero { @@ -78,10 +80,13 @@ .feature-item { font-family: 'Lato', sans-serif; - font-weight: 300; + font-weight: 1.1rem; box-sizing: border-box; - padding: 0 15px; - word-break: break-word + word-break: break-word; + background-color: #8e8e8e25; + margin:10px; + padding: 8px; + border-radius: 10px; } .feature-item h2 { @@ -92,9 +97,9 @@ overflow: hidden; text-overflow: ellipsis; line-height: normal; - margin-top: 20px; - margin-bottom: 10px; font-family: inherit; + padding: 2px 0; + margin: 0; } .feature-item p { @@ -103,10 +108,17 @@ text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; color: var(--md-default-fg-color); - margin: 0 0 10px; + margin: 0; display: block; } + #_1 { + margin: 0; + } + #installation { + margin: 0 0 .64em + } + html{scroll-behavior:smooth} @media screen and (max-width:30em) { .tx-hero h1 { font-size: 1.4rem @@ -230,10 +242,10 @@

Ocean Data Parser

A python tool to parse common ocean data proprietary formats.

- + Get started - + Go to GitHub
@@ -272,11 +284,4 @@

Notebook examples and api documentation available

- -
-
- - -{% endblock %} -{% block content %}{% endblock %} -{% block footer %}{% endblock %} \ No newline at end of file +{% endblock %} \ No newline at end of file diff --git a/docs/user_guide/odpy.md b/docs/user_guide/cli.md similarity index 70% rename from docs/user_guide/odpy.md rename to docs/user_guide/cli.md index e1bda1d3..f8521788 100644 --- a/docs/user_guide/odpy.md +++ b/docs/user_guide/cli.md @@ -1,4 +1,7 @@ -The ocean-data-parser provides a `odpy [ODPY OPTIONS] [METHOD] [method OPTIONS]` tool. +--- +title: odpy CLI +--- +# Command Line Interface ::: mkdocs-click :module: ocean_data_parser.cli @@ -6,11 +9,8 @@ The ocean-data-parser provides a `odpy [ODPY OPTIONS] [METHOD] [method OPTIONS]` :depth: 0 !!! Tip "Environment Variables" - All the inputs available within the `odpy` command can be defined through environment variables: `ODPY_*`, `ODPY_CONVERT_*` and `ODPY_COMPILE_*` respectively. + All the inputs available within the `odpy` command can be defined through environment variables: `ODPY_*`, `ODPY_CONVERT_*` and `ODPY_COMPILE_*` respectively. Example: - `ODPY_LOG_LEVEL=WARNING` will force `odpy` to log only the warning events. - `ODPY_CONVERT_OUTPUT_PATH=output` will force `odpy convert` to output to the the local directory `./output/` - -!!! Warning - An argument take priority over an environment variable. \ No newline at end of file diff --git a/docs/user_guide/index.md b/docs/user_guide/index.md index d3a1946e..3749639b 100644 --- a/docs/user_guide/index.md +++ b/docs/user_guide/index.md @@ -1,9 +1,95 @@ -The ocean-data-parser can be used through two main methods: +# User Guide -- [command line interface `odpy`: Ideal for conversions, CI tools.](odpy.md) -- `from ocean_data_parser import ...`: To integrate within a python script or method. - + [`from ocean_data_parser import read.file`](read.md) - + [`from ocean_data_parser.parsers import ..`](parsers/how-to.md) +## How to +The ocean-data-parser can be used different ways: +- [ ] [Command Line interface](cli.md) ---8<-- "docs/user_guide/parsers/parser-list.md" \ No newline at end of file +- [ ] Python Import + + - [`from ocean_data_parser import read.file`](read.md) + - [`from ocean_data_parser.parsers import ...`](parsers/index.md) + +## Installation + +Install the github development version via pip. You can also install a specific branch, tag or hash by following the present examples: + +=== "Default" + + ```console + # install the latest development branch version + pip install git+git+https://github.com/cioos-siooc/ocean-data-parser.git + ``` + +=== "Specific branch" + + ``` shell + # if branch=test-branch + pip install git+git+https://github.com/cioos-siooc/ocean-data-parser.git@test-branch + ``` + +=== "Specific tag" + + ``` shell + # if `tag=v1.2` + pip install git+git+https://github.com/cioos-siooc/ocean-data-parser.git@v1.2 + ``` + +=== "Specific hash" + + ``` shell + # if hash=2927346f4c513a217ac8ad076e494dd1adbf70e1 + pip install git+git+https://github.com/cioos-siooc/ocean-data-parser.git@2927346f4c513a217ac8ad076e494dd1adbf70e1 + ``` + +## Integration within a project + +We recommmand fixing the `ocean_data_parser` used within your project since this +package api is still subject to change over the future versions. + +You can achieve that either: + +- with `pip` by generating a `requirement.txt` +- (recommanded) with [`poetry`](https://python-poetry.org/) by creating a `pyproject.toml` and `poetry.lock` file. + +=== "poetry" + + ``` shell + mkdir my_project + cd my_project + + # initialize poetry package + poetry init + # add package via instruction + # or with the following command once the pyproject.toml is generated + poetry add git+https://github.com/cioos-siooc/ocean-data-parser.git + + # install packages in local environment. + poetry install + + # A new file pyproject.toml should be generated with a poetry.lock file + # which list the specific version of each package installed. + ``` + +=== "pip" + + ``` shell + mkdir my_project + cd my_project + + pip install git+https://github.com/cioos-siooc/ocean-data-parser.git + pip freeze > requirements.txt + + # use next time + pip install -r requirement.txt + ``` + +!!! info "Poetry package-mode" + + If you only use poetry for dependancy management and tracking versions used + in your project. Add the following snippet in our `pyproject.toml`: + + ``` toml + [tool.poetry] + package-mode = false + ``` diff --git a/docs/user_guide/parsers/amundsen.md b/docs/user_guide/parsers/amundsen.md index 5a165698..32c9ac38 100644 --- a/docs/user_guide/parsers/amundsen.md +++ b/docs/user_guide/parsers/amundsen.md @@ -2,4 +2,4 @@ ## Vocabulary ---8<-- "docs/user_guide/vocabularies/amundsen-int.md" \ No newline at end of file +--8<-- "docs/user_guide/vocabularies/amundsen-int.md" diff --git a/docs/user_guide/parsers/dfo/ios.md b/docs/user_guide/parsers/dfo/ios.md index 6db6968f..e793c30f 100644 --- a/docs/user_guide/parsers/dfo/ios.md +++ b/docs/user_guide/parsers/dfo/ios.md @@ -2,4 +2,4 @@ ## IOS Vocabulary ---8<-- "docs/user_guide/vocabularies/dfo-ios-shell.md" \ No newline at end of file +--8<-- "docs/user_guide/vocabularies/dfo-ios-shell.md" diff --git a/docs/user_guide/parsers/dfo/nafc.md b/docs/user_guide/parsers/dfo/nafc.md index ffb17b73..5e5a4ee3 100644 --- a/docs/user_guide/parsers/dfo/nafc.md +++ b/docs/user_guide/parsers/dfo/nafc.md @@ -2,4 +2,4 @@ ## p-files vocabulary ---8<-- "docs/user_guide/vocabularies/dfo-nafc-p-files.md" \ No newline at end of file +--8<-- "docs/user_guide/vocabularies/dfo-nafc-p-files.md" diff --git a/docs/user_guide/parsers/dfo/odf.md b/docs/user_guide/parsers/dfo/odf.md index 4fd77008..0848f375 100644 --- a/docs/user_guide/parsers/dfo/odf.md +++ b/docs/user_guide/parsers/dfo/odf.md @@ -1,5 +1,5 @@ :::ocean_data_parser.parsers.dfo.odf -## IOS Vocabulary +## ODF Vocabulary ---8<-- "docs/user_guide/vocabularies/dfo-odf.md" \ No newline at end of file +--8<-- "docs/user_guide/vocabularies/dfo-odf.md" diff --git a/docs/user_guide/parsers/electricblue.md b/docs/user_guide/parsers/electricblue.md index 72b93f47..abcb52e1 100644 --- a/docs/user_guide/parsers/electricblue.md +++ b/docs/user_guide/parsers/electricblue.md @@ -1 +1 @@ -::: ocean_data_parser.parsers.electricblue \ No newline at end of file +::: ocean_data_parser.parsers.electricblue diff --git a/docs/user_guide/parsers/header-index.md b/docs/user_guide/parsers/header-index.md new file mode 100644 index 00000000..dc5d35da --- /dev/null +++ b/docs/user_guide/parsers/header-index.md @@ -0,0 +1,15 @@ +# Ocean Data Parsers + +Each parser can also be imported by itself: + +```python +from ocean_data_parser.parsers import dfo.odf + +ds = dfo.odf.bio_odf('file_path') +``` + +## Parsers available + +Ocean Data Parser includes the following data format parsers: + +{{ parsers_list }} \ No newline at end of file diff --git a/docs/user_guide/parsers/how-to.md b/docs/user_guide/parsers/how-to.md deleted file mode 100644 index 6eb8ede1..00000000 --- a/docs/user_guide/parsers/how-to.md +++ /dev/null @@ -1,7 +0,0 @@ -Each parser can also be imported by itself. - -```python -from ocean_data_parser.parsers import dfo.odf - -ds = dfo.odf.bio_odf('file_path') -``` \ No newline at end of file diff --git a/docs/user_guide/parsers/index.md b/docs/user_guide/parsers/index.md new file mode 100644 index 00000000..48a5ace2 --- /dev/null +++ b/docs/user_guide/parsers/index.md @@ -0,0 +1,78 @@ +# Ocean Data Parsers + +Each parser can also be imported by itself: + +```python +from ocean_data_parser.parsers import dfo.odf + +ds = dfo.odf.bio_odf('file_path') +``` + +## Parsers available + +Ocean Data Parser includes the following data format parsers: + +[AMUNDSEN](amundsen.md) + +- [amundsen.int_format](amundsen.md#ocean_data_parser.parsers.amundsen.int_format) + +[DFO.IOS](dfo/ios.md) + +- [dfo.ios.shell](dfo/ios.md#ocean_data_parser.parsers.dfo.ios.shell) +- [dfo.ios.shell](dfo/ios.md#ocean_data_parser.parsers.dfo.ios.shell) + +[DFO.NAFC](dfo/nafc.md) + +- [dfo.nafc.pcnv](dfo/nafc.md#ocean_data_parser.parsers.dfo.nafc.pcnv) +- [dfo.nafc.pfile](dfo/nafc.md#ocean_data_parser.parsers.dfo.nafc.pfile) + +[DFO.ODF](dfo/odf.md) + +- [dfo.odf.bio_odf](dfo/odf.md#ocean_data_parser.parsers.dfo.odf.bio_odf) +- [dfo.odf.mli_odf](dfo/odf.md#ocean_data_parser.parsers.dfo.odf.mli_odf) +- [dfo.odf.mli_odf](dfo/odf.md#ocean_data_parser.parsers.dfo.odf.mli_odf) + +[ELECTRICBLUE](electricblue.md) + +- [electricblue.csv](electricblue.md#ocean_data_parser.parsers.electricblue.csv) +- [electricblue.log_csv](electricblue.md#ocean_data_parser.parsers.electricblue.log_csv) + +[NETCDF](netcdf.md) + +- [netcdf](netcdf.md) + +[NMEA](nmea.md) + +- [nmea.file](nmea.md#ocean_data_parser.parsers.nmea.file) + +[ONSET](onset.md) + +- [onset.csv](onset.md#ocean_data_parser.parsers.onset.csv) + +[PME](pme.md) + +- [pme.minidot_txt](pme.md#ocean_data_parser.parsers.pme.minidot_txt) + +[RBR](rbr.md) + +- [rbr.rtext](rbr.md#ocean_data_parser.parsers.rbr.rtext) + +[SEABIRD](seabird.md) + +- [seabird.btl](seabird.md#ocean_data_parser.parsers.seabird.btl) +- [seabird.cnv](seabird.md#ocean_data_parser.parsers.seabird.cnv) + +[STAR_ODDI](star-oddi.md) + +- [star_oddi.DAT](star-oddi.md#ocean_data_parser.parsers.star_oddi.DAT) + +[SUNBURST](sunburst.md) + +- [sunburst.superCO2_notes](sunburst.md#ocean_data_parser.parsers.sunburst.superCO2_notes) +- [sunburst.superCO2](sunburst.md#ocean_data_parser.parsers.sunburst.superCO2) + +[VAN_ESSEN_INSTRUMENTS](van-essen-instruments.md) + +- [van_essen_instruments.mon](van-essen-instruments.md#ocean_data_parser.parsers.van_essen_instruments.mon) + + diff --git a/docs/user_guide/parsers/netcdf.md b/docs/user_guide/parsers/netcdf.md new file mode 100644 index 00000000..4ca94f75 --- /dev/null +++ b/docs/user_guide/parsers/netcdf.md @@ -0,0 +1,5 @@ +# NetCDF +`Ocean Data Parser` uses `xarray.open_dataset(...)` to load a NetCDF file. + +See [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html) +documentation for more details. \ No newline at end of file diff --git a/docs/user_guide/parsers/nmea.md b/docs/user_guide/parsers/nmea.md index 7265a62c..a760aefd 100644 --- a/docs/user_guide/parsers/nmea.md +++ b/docs/user_guide/parsers/nmea.md @@ -1 +1 @@ -::: ocean_data_parser.parsers.nmea \ No newline at end of file +::: ocean_data_parser.parsers.nmea diff --git a/docs/user_guide/parsers/onset.md b/docs/user_guide/parsers/onset.md index c4a8bd61..b59e117d 100644 --- a/docs/user_guide/parsers/onset.md +++ b/docs/user_guide/parsers/onset.md @@ -1 +1 @@ -::: ocean_data_parser.parsers.onset \ No newline at end of file +::: ocean_data_parser.parsers.onset diff --git a/docs/user_guide/parsers/pme.md b/docs/user_guide/parsers/pme.md index d1836cb0..0cb324b0 100644 --- a/docs/user_guide/parsers/pme.md +++ b/docs/user_guide/parsers/pme.md @@ -1 +1 @@ -::: ocean_data_parser.parsers.pme \ No newline at end of file +::: ocean_data_parser.parsers.pme diff --git a/docs/user_guide/parsers/rbr.md b/docs/user_guide/parsers/rbr.md index 74233271..d59f8d1b 100644 --- a/docs/user_guide/parsers/rbr.md +++ b/docs/user_guide/parsers/rbr.md @@ -1 +1 @@ -::: ocean_data_parser.parsers.rbr \ No newline at end of file +::: ocean_data_parser.parsers.rbr diff --git a/docs/user_guide/parsers/star-oddi.md b/docs/user_guide/parsers/star-oddi.md index fc32608f..affa4bf8 100644 --- a/docs/user_guide/parsers/star-oddi.md +++ b/docs/user_guide/parsers/star-oddi.md @@ -1 +1 @@ -::: ocean_data_parser.parsers.star_oddi \ No newline at end of file +::: ocean_data_parser.parsers.star_oddi diff --git a/docs/user_guide/parsers/sunburst.md b/docs/user_guide/parsers/sunburst.md index c5acf553..657da314 100644 --- a/docs/user_guide/parsers/sunburst.md +++ b/docs/user_guide/parsers/sunburst.md @@ -1 +1 @@ -:::ocean_data_parser.parsers.sunburst \ No newline at end of file +:::ocean_data_parser.parsers.sunburst diff --git a/docs/user_guide/parsers/van-essen-instruments.md b/docs/user_guide/parsers/van-essen-instruments.md new file mode 100644 index 00000000..9fa76e3a --- /dev/null +++ b/docs/user_guide/parsers/van-essen-instruments.md @@ -0,0 +1 @@ +::: ocean_data_parser.parsers.van_essen_instruments diff --git a/docs/user_guide/parsers/van-essen-intruments.md b/docs/user_guide/parsers/van-essen-intruments.md deleted file mode 100644 index 224f5dd5..00000000 --- a/docs/user_guide/parsers/van-essen-intruments.md +++ /dev/null @@ -1 +0,0 @@ -::: ocean_data_parser.parsers.van_essen_instruments \ No newline at end of file diff --git a/docs/user_guide/read.md b/docs/user_guide/read.md index 3624b517..06ec90e3 100644 --- a/docs/user_guide/read.md +++ b/docs/user_guide/read.md @@ -4,4 +4,4 @@ options: members: - file - - detect_file_format \ No newline at end of file + - detect_file_format diff --git a/mkdocs.yml b/mkdocs.yml index 3b17015d..0736b48e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,6 @@ +--- site_name: Ocean Data Parser -site_url: https://cioos-siooc.github.io/ocean-data-parser/ +site_url: https://cioos-siooc.github.io/ocean-data-parser/ repo_name: ocean-data-parser repo_url: https://github.com/cioos-siooc/ocean-data-parser @@ -32,17 +33,19 @@ theme: - navigation.footer - navigation.top - navigation.tracking + - navigation.indexes - toc.integrate - toc.follow - navigation.tabs - navigation.tabs.sticky - content.code.copy - content.code.select + - content.tabs.link plugins: - mkdocstrings - mkdocs-jupyter: - ignore_h1_titles: True + ignore_h1_titles: true - mike: # These fields are all optional; the defaults are as below... canonical_version: main @@ -51,79 +54,81 @@ plugins: on_pre_build: "docs.scripts.hooks:on_pre_build" - search - material-plausible - + markdown_extensions: - - mkdocs-click - - admonition - - attr_list - - def_list - - pymdownx.tasklist: - custom_checkbox: true - - pymdownx.highlight: - anchor_linenums: true - line_spans: __span - pygments_lang_class: true - - pymdownx.inlinehilite - - pymdownx.snippets - - pymdownx.superfences - - admonition - - pymdownx.details - - pymdownx.superfences - - attr_list - - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg - + - mkdocs-click + - admonition + - attr_list + - def_list + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + - admonition + - pymdownx.details + - pymdownx.superfences + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.tabbed: + alternate_style: true + - toc: + permalink: true + nav: - Home: index.md - - Get Started: - - Installation: get_started/installation.md - - How to: get_started/how-to.md - - Development: get_started/development.md - - Contributions: get_started/contributions.md - User Guide: - - main: user_guide/index.md - - odpy (cli): user_guide/odpy.md - - ocean_data_parser.read: user_guide/read.md - - ocean_data_parser.parsers: - - How to: user_guide/parsers/how-to.md - - amundsen: user_guide/parsers/amundsen.md - - dfo: - - odf: user_guide/parsers/dfo/odf.md - - ios: user_guide/parsers/dfo/ios.md - - nafc: user_guide/parsers/dfo/nafc.md - - electricblue: user_guide/parsers/electricblue.md - - nmea: user_guide/parsers/nmea.md - - onset: user_guide/parsers/onset.md - - pme: user_guide/parsers/pme.md - - rbr: user_guide/parsers/rbr.md - - seabird: user_guide/parsers/seabird.md - - star_oddi: user_guide/parsers/star-oddi.md - - sunburst: user_guide/parsers/sunburst.md - - van_essen_instruments: user_guide/parsers/van-essen-intruments.md - - vocabularies: - - amundsen: user_guide/vocabularies/amundsen-int.md - - dfo.odf: user_guide/vocabularies/dfo-odf.md - - dfo.ios.shell: user_guide/vocabularies/dfo-ios-shell.md - - dfo.nafc.pfile: user_guide/vocabularies/dfo-nafc-p-files.md - - seabird: user_guide/vocabularies/seabird.md - # - Metadata: # TODO + - User Guide: user_guide/index.md + - Command Line Interface (odpy): user_guide/cli.md + - ocean_data_parser.read.file: user_guide/read.md + - ocean_data_parser.parsers: + - user_guide/parsers/index.md + - amundsen: user_guide/parsers/amundsen.md + - dfo: + - dfo.ios: user_guide/parsers/dfo/ios.md + - dfo.nafc: user_guide/parsers/dfo/nafc.md + - dfo.odf: user_guide/parsers/dfo/odf.md + - electricblue: user_guide/parsers/electricblue.md + - nmea: user_guide/parsers/nmea.md + - netcdf: user_guide/parsers/netcdf.md + - onset: user_guide/parsers/onset.md + - pme: user_guide/parsers/pme.md + - rbr: user_guide/parsers/rbr.md + - seabird: user_guide/parsers/seabird.md + - star_oddi: user_guide/parsers/star-oddi.md + - sunburst: user_guide/parsers/sunburst.md + - van_essen_instruments: user_guide/parsers/van-essen-instruments.md + - vocabularies: + - amundsen: user_guide/vocabularies/amundsen-int.md + - dfo.odf: user_guide/vocabularies/dfo-odf.md + - dfo.ios.shell: user_guide/vocabularies/dfo-ios-shell.md + - dfo.nafc.pfile: user_guide/vocabularies/dfo-nafc-p-files.md + - seabird: user_guide/vocabularies/seabird.md + # - Metadata: # TODO # - CF: metadata/cf.md # - NERC: metadata/nerc.md # - Polar Data Catalog: metadata/pdc.md - Example Notebooks: - - Amundsen INT example: notebooks/amundsen_int_parser_example.ipynb - - DFO BIO ODF example: notebooks/dfo_bio_odf_parser_example.ipynb - - DFO BIO NAFC Pfiles example: notebooks/dfo_nafc_p_file_parser_example.ipynb - - NMEA example: notebooks/nmea_parser_example.ipynb - - Seabird CNV and BTL files example: notebooks/seabird_cnv_and_btl_parser_example.ipynb + - Amundsen INT example: notebooks/amundsen_int_parser_example.ipynb + - DFO BIO ODF example: notebooks/dfo_bio_odf_parser_example.ipynb + - DFO BIO NAFC Pfiles example: notebooks/dfo_nafc_p_file_parser_example.ipynb + - NMEA example: notebooks/nmea_parser_example.ipynb + - Seabird CNV and BTL files example: notebooks/seabird_cnv_and_btl_parser_example.ipynb + - Development: development.md + - Release notes: release-notes.md + extra: version: provider: mike social: - - icon: fontawesome/brands/github + - icon: fontawesome/brands/github link: https://github.com/cioos-siooc generator: false analytics: diff --git a/notebooks/dfo_nafc_p_file_parser_example.ipynb b/notebooks/dfo_nafc_p_file_parser_example.ipynb index d3d79c0b..05d222fa 100644 --- a/notebooks/dfo_nafc_p_file_parser_example.ipynb +++ b/notebooks/dfo_nafc_p_file_parser_example.ipynb @@ -15,7 +15,7 @@ "outputs": [], "source": [ "# google colab only, install ocean-data-parser\n", - "%pip install git+https://github.com/cioos-siooc/ocean-data-parser.git@add-dfo-nl-p-file-parser" + "%pip install git+https://github.com/cioos-siooc/ocean-data-parser.git@development" ] }, { diff --git a/notebooks/nmea_parser_example.ipynb b/notebooks/nmea_parser_example.ipynb index 2285ad59..c7a2c577 100644 --- a/notebooks/nmea_parser_example.ipynb +++ b/notebooks/nmea_parser_example.ipynb @@ -1,1239 +1,1251 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "y65TkqNxzd1a", - "outputId": "47b1b249-2bef-404e-ab8b-3e9e1a214a56" - }, - "outputs": [], - "source": [ - "%pip install git+https://github.com/cioos-siooc/ocean-data-parser.git\n", - "\n", - "%pip install folium \n", - "%pip install plotly" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Install packages" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%pip install git+https://github.com/cioos-siooc/ocean-data-parser.git\n", - "\n", - "%pip install folium \n", - "%pip install plotly" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Imports" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "id": "6z9zWjOjzkEG" - }, - "outputs": [], - "source": [ - "from ocean_data_parser.parsers import nmea\n", - "import pandas as pd\n", - "import xarray as xr\n", - "from glob import glob\n", - "from tqdm import tqdm\n", - "import requests\n", - "import folium\n" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "id": "GHDoNtsD6J8Z" - }, - "source": [ - "# Download test data \n", - "NMEA test data is available here:\n", - "\n", - "https://github.com/HakaiInstitute/ocean-data-parser/tree/main/tests/parsers_test_files/nmea" - ] + "id": "y65TkqNxzd1a", + "outputId": "47b1b249-2bef-404e-ab8b-3e9e1a214a56" + }, + "outputs": [], + "source": [ + "%pip install git+https://github.com/cioos-siooc/ocean-data-parser.git\n", + "\n", + "%pip install folium\n", + "%pip install plotly" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Install packages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%pip install git+https://github.com/cioos-siooc/ocean-data-parser.git\n", + "\n", + "%pip install folium\n", + "%pip install plotly" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "6z9zWjOjzkEG" + }, + "outputs": [], + "source": [ + "from ocean_data_parser.parsers import nmea\n", + "import pandas as pd\n", + "import xarray as xr\n", + "from glob import glob\n", + "from tqdm import tqdm\n", + "import requests\n", + "import folium" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "id": "GHDoNtsD6J8Z" + }, + "source": [ + "# Download test data \n", + "NMEA test data is available here:\n", + "\n", + "https://github.com/HakaiInstitute/ocean-data-parser/tree/main/tests/parsers_test_files/nmea" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "3kCx494v436r", - "outputId": "078a7430-91f4-4202-de01-18e969ad75d7" - }, - "outputs": [], - "source": [ - "# retrieve raw data\n", - "\n", - "def download_file_from_github(raw_url):\n", - " \"\"\"Download a github raw file localy\"\"\"\n", - " file_path = raw_url.split('/')[-1]\n", - " response = requests.get(raw_url)\n", - " with open(file_path, \"w\") as f:\n", - " f.write(response.text)\n", - " return file_path\n", - "\n", - "nmea_files = [\"https://raw.githubusercontent.com/cioos-siooc/ocean-data-parser/development/tests/parsers_test_files/nmea/seaspan/nmeadata-2022-07-04_00.txt\",\n", - "\"https://raw.githubusercontent.com/cioos-siooc/ocean-data-parser/development/tests/parsers_test_files/nmea/seaspan/nmeadata-2022-07-04_01.txt\",\n", - "\"https://raw.githubusercontent.com/cioos-siooc/ocean-data-parser/development/tests/parsers_test_files/nmea/seaspan/nmeadata-2022-07-04_02.txt\",\n", - "\"https://raw.githubusercontent.com/cioos-siooc/ocean-data-parser/development/tests/parsers_test_files/nmea/seaspan/nmeadata-2022-07-04_03.txt\"]\n", - "\n", - "files = [download_file_from_github(file) for file in nmea_files]" - ] + "id": "3kCx494v436r", + "outputId": "078a7430-91f4-4202-de01-18e969ad75d7" + }, + "outputs": [], + "source": [ + "# retrieve raw data\n", + "\n", + "\n", + "def download_file_from_github(raw_url):\n", + " \"\"\"Download a github raw file localy\"\"\"\n", + " file_path = raw_url.split(\"/\")[-1]\n", + " response = requests.get(raw_url)\n", + " with open(file_path, \"w\") as f:\n", + " f.write(response.text)\n", + " return file_path\n", + "\n", + "\n", + "nmea_files = [\n", + " \"https://raw.githubusercontent.com/cioos-siooc/ocean-data-parser/development/tests/parsers_test_files/nmea/seaspan/nmeadata-2022-07-04_00.txt\",\n", + " \"https://raw.githubusercontent.com/cioos-siooc/ocean-data-parser/development/tests/parsers_test_files/nmea/seaspan/nmeadata-2022-07-04_01.txt\",\n", + " \"https://raw.githubusercontent.com/cioos-siooc/ocean-data-parser/development/tests/parsers_test_files/nmea/seaspan/nmeadata-2022-07-04_02.txt\",\n", + " \"https://raw.githubusercontent.com/cioos-siooc/ocean-data-parser/development/tests/parsers_test_files/nmea/seaspan/nmeadata-2022-07-04_03.txt\",\n", + "]\n", + "\n", + "files = [download_file_from_github(file) for file in nmea_files]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load NMEA data \n", + "\n", + "Here we load the different NMEA files and convert them a pandas dataframe." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 455 }, + "id": "0oSOr5tx4XY3", + "outputId": "787d2983-31a5-4a1d-815a-16f4879d12d5" + }, + "outputs": [ { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Load NMEA data \n", - "\n", - "Here we load the different NMEA files and convert them a pandas dataframe." - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "Import nmea files: 100%|██████████| 4/4 [00:00<00:00, 5.21file/s]\n" + ] }, { - "cell_type": "code", - "execution_count": 50, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 455 - }, - "id": "0oSOr5tx4XY3", - "outputId": "787d2983-31a5-4a1d-815a-16f4879d12d5" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Import nmea files: 100%|██████████| 4/4 [00:00<00:00, 5.21file/s]\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-              "Dimensions:                                (index: 28800)\n",
-              "Coordinates:\n",
-              "  * index                                  (index) int64 0 1 2 ... 7198 7199\n",
-              "Data variables: (12/50)\n",
-              "    row                                    (index) float64 0.0 1.0 ... 7.199e+03\n",
-              "    prefix                                 (index) object '20220704 020001+00...\n",
-              "    talker                                 (index) object 'II' 'HC' ... 'WI'\n",
-              "    sentence_type                          (index) object 'MDA' 'HDT' ... 'MWV'\n",
-              "    subtype                                (index) object None None ... None\n",
-              "    manufacturer                           (index) object None None ... None\n",
-              "    ...                                     ...\n",
-              "    wind_angle                             (index) float64 nan nan ... nan 339.8\n",
-              "    reference                              (index) object None None ... None 'R'\n",
-              "    wind_speed                             (index) float64 nan nan ... nan 1.19\n",
-              "    wind_speed_units                       (index) object None None ... None 'N'\n",
-              "    wind_speed_relative_to_platform_knots  (index) float64 nan nan ... nan 1.19\n",
-              "    wind_direction_relative_to_platform    (index) float64 nan nan ... nan 339.8
" - ], - "text/plain": [ - "\n", - "Dimensions: (index: 28800)\n", - "Coordinates:\n", - " * index (index) int64 0 1 2 ... 7198 7199\n", - "Data variables: (12/50)\n", - " row (index) float64 0.0 1.0 ... 7.199e+03\n", - " prefix (index) object '20220704 020001+00...\n", - " talker (index) object 'II' 'HC' ... 'WI'\n", - " sentence_type (index) object 'MDA' 'HDT' ... 'MWV'\n", - " subtype (index) object None None ... None\n", - " manufacturer (index) object None None ... None\n", - " ... ...\n", - " wind_angle (index) float64 nan nan ... nan 339.8\n", - " reference (index) object None None ... None 'R'\n", - " wind_speed (index) float64 nan nan ... nan 1.19\n", - " wind_speed_units (index) object None None ... None 'N'\n", - " wind_speed_relative_to_platform_knots (index) float64 nan nan ... nan 1.19\n", - " wind_direction_relative_to_platform (index) float64 nan nan ... nan 339.8" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                (index: 28800)\n",
+       "Coordinates:\n",
+       "  * index                                  (index) int64 0 1 2 ... 7198 7199\n",
+       "Data variables: (12/50)\n",
+       "    row                                    (index) float64 0.0 1.0 ... 7.199e+03\n",
+       "    prefix                                 (index) object '20220704 020001+00...\n",
+       "    talker                                 (index) object 'II' 'HC' ... 'WI'\n",
+       "    sentence_type                          (index) object 'MDA' 'HDT' ... 'MWV'\n",
+       "    subtype                                (index) object None None ... None\n",
+       "    manufacturer                           (index) object None None ... None\n",
+       "    ...                                     ...\n",
+       "    wind_angle                             (index) float64 nan nan ... nan 339.8\n",
+       "    reference                              (index) object None None ... None 'R'\n",
+       "    wind_speed                             (index) float64 nan nan ... nan 1.19\n",
+       "    wind_speed_units                       (index) object None None ... None 'N'\n",
+       "    wind_speed_relative_to_platform_knots  (index) float64 nan nan ... nan 1.19\n",
+       "    wind_direction_relative_to_platform    (index) float64 nan nan ... nan 339.8
" ], - "source": [ - "nmea_files = glob(\"nmeadata-*.txt\")\n", - "nmea_parsed = [\n", - " nmea.file(file) for file in tqdm(nmea_files, desc=\"Import nmea files\", unit=\"file\")\n", - "]\n", - "# Aggregate all files\n", - "ds = xr.concat(nmea_parsed, dim=\"index\")\n", - "ds" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Convert prefix of NMEA to datetime" + "text/plain": [ + "\n", + "Dimensions: (index: 28800)\n", + "Coordinates:\n", + " * index (index) int64 0 1 2 ... 7198 7199\n", + "Data variables: (12/50)\n", + " row (index) float64 0.0 1.0 ... 7.199e+03\n", + " prefix (index) object '20220704 020001+00...\n", + " talker (index) object 'II' 'HC' ... 'WI'\n", + " sentence_type (index) object 'MDA' 'HDT' ... 'MWV'\n", + " subtype (index) object None None ... None\n", + " manufacturer (index) object None None ... None\n", + " ... ...\n", + " wind_angle (index) float64 nan nan ... nan 339.8\n", + " reference (index) object None None ... None 'R'\n", + " wind_speed (index) float64 nan nan ... nan 1.19\n", + " wind_speed_units (index) object None None ... None 'N'\n", + " wind_speed_relative_to_platform_knots (index) float64 nan nan ... nan 1.19\n", + " wind_direction_relative_to_platform (index) float64 nan nan ... nan 339.8" ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [], - "source": [ - "# In this case the each rows prefix prior to the NMEA string is a timestamp, convert it to a datetime object\n", - "ds[\"computer_time\"] = (ds['prefix'].dims,pd.Series(pd.to_datetime(ds[\"prefix\"].str.strip(), format='%Y%m%d %H%M%S%z')))\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Reseample NMEA data to 2s" - ] - }, + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nmea_files = glob(\"nmeadata-*.txt\")\n", + "nmea_parsed = [\n", + " nmea.file(file) for file in tqdm(nmea_files, desc=\"Import nmea files\", unit=\"file\")\n", + "]\n", + "# Aggregate all files\n", + "ds = xr.concat(nmea_parsed, dim=\"index\")\n", + "ds" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Convert prefix of NMEA to datetime" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [], + "source": [ + "# In this case the each rows prefix prior to the NMEA string is a timestamp, convert it to a datetime object\n", + "ds[\"computer_time\"] = (\n", + " ds[\"prefix\"].dims,\n", + " pd.Series(pd.to_datetime(ds[\"prefix\"].str.strip(), format=\"%Y%m%d %H%M%S%z\")),\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Reseample NMEA data to 2s" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 103, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-              "Dimensions:                                (computer_time: 7200)\n",
-              "Coordinates:\n",
-              "  * computer_time                          (computer_time) datetime64[ns] 202...\n",
-              "Data variables: (12/50)\n",
-              "    row                                    (computer_time) float64 0.0 ... 7....\n",
-              "    prefix                                 (computer_time) <U22 '20220704 000...\n",
-              "    talker                                 (computer_time) <U2 'II' ... 'II'\n",
-              "    sentence_type                          (computer_time) <U3 'MDA' ... 'MDA'\n",
-              "    subtype                                (computer_time) object None ... None\n",
-              "    manufacturer                           (computer_time) object None ... None\n",
-              "    ...                                     ...\n",
-              "    wind_angle                             (computer_time) float64 284.6 ... ...\n",
-              "    reference                              (computer_time) <U1 'R' 'R' ... 'R'\n",
-              "    wind_speed                             (computer_time) float64 2.68 ... 2...\n",
-              "    wind_speed_units                       (computer_time) <U1 'N' 'N' ... 'N'\n",
-              "    wind_speed_relative_to_platform_knots  (computer_time) float64 2.68 ... 2...\n",
-              "    wind_direction_relative_to_platform    (computer_time) float64 284.6 ... ...
" - ], - "text/plain": [ - "\n", - "Dimensions: (computer_time: 7200)\n", - "Coordinates:\n", - " * computer_time (computer_time) datetime64[ns] 202...\n", - "Data variables: (12/50)\n", - " row (computer_time) float64 0.0 ... 7....\n", - " prefix (computer_time) \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                (computer_time: 7200)\n",
+       "Coordinates:\n",
+       "  * computer_time                          (computer_time) datetime64[ns] 202...\n",
+       "Data variables: (12/50)\n",
+       "    row                                    (computer_time) float64 0.0 ... 7....\n",
+       "    prefix                                 (computer_time) <U22 '20220704 000...\n",
+       "    talker                                 (computer_time) <U2 'II' ... 'II'\n",
+       "    sentence_type                          (computer_time) <U3 'MDA' ... 'MDA'\n",
+       "    subtype                                (computer_time) object None ... None\n",
+       "    manufacturer                           (computer_time) object None ... None\n",
+       "    ...                                     ...\n",
+       "    wind_angle                             (computer_time) float64 284.6 ... ...\n",
+       "    reference                              (computer_time) <U1 'R' 'R' ... 'R'\n",
+       "    wind_speed                             (computer_time) float64 2.68 ... 2...\n",
+       "    wind_speed_units                       (computer_time) <U1 'N' 'N' ... 'N'\n",
+       "    wind_speed_relative_to_platform_knots  (computer_time) float64 2.68 ... 2...\n",
+       "    wind_direction_relative_to_platform    (computer_time) float64 284.6 ... ...
" ], - "source": [ - "ds_resampled = ds.swap_dims({'index':'computer_time'}).sortby('computer_time').resample({\"computer_time\":'2s'}).first()\n", - "ds_resampled" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Plot Resulting Data" + "text/plain": [ + "\n", + "Dimensions: (computer_time: 7200)\n", + "Coordinates:\n", + " * computer_time (computer_time) datetime64[ns] 202...\n", + "Data variables: (12/50)\n", + " row (computer_time) float64 0.0 ... 7....\n", + " prefix (computer_time) , ,\n", - " ], dtype=object)" - ] - }, - "execution_count": 102, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABRQAAAK3CAYAAAD0ycMmAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3xT9f7H8Xea7s0ulamyREGWiIqyFFFRcOEEt/6u4EC9Xq4LvCpucOC8COpVcYKKCirKlI0sQUBkQylltLSFruT3x2lmkzRpk6bj9Xw8+kjOOd/zPd90pMknn+/3Y7JarVYBAAAAAAAAgB8iwj0AAAAAAAAAADUHAUUAAAAAAAAAfiOgCAAAAAAAAMBvBBQBAAAAAAAA+I2AIgAAAAAAAAC/EVAEAAAAAAAA4DcCigAAAAAAAAD8RkARAAAAAAAAgN8iwz2AYLBYLNq7d6+SkpJkMpnCPRwAAAAAAACgRrFarTp69KjS09MVEeE7B7FWBBT37t2r5s2bh3sYAAAAAAAAQI22a9cuNWvWzGebWhFQTEpKkmQ84OTk5DCPBgAAAAAAAKhZcnJy1Lx5c3uczZdaEVC0TXNOTk4moAgAAAAAAABUkD/LCVKUBQAAAAAAAIDfAg4ozp8/X4MHD1Z6erpMJpNmzJjhte1dd90lk8mkiRMn+uxz7NixMplMLl/t27cPdGgAAAAAAAAAQizgKc95eXnq3LmzbrnlFl1++eVe202fPl1LlixRenq6X/127NhRP//8s2NgkbViNjYAAAAAAECtVVJSoqKionAPA36KioqS2WyudD8BR+0GDRqkQYMG+WyzZ88ejRo1SrNnz9bFF1/s30AiI5WWlhbocFDN7M85rg37cuzbnU5IUYPEmDCOCAAAAAAABJvValVGRoaOHDkS7qEgQKmpqUpLS/NrrURvgp4GaLFYdOONN+qhhx5Sx44d/T5vy5YtSk9PV2xsrHr16qXx48erRYsWHtsWFBSooKDAvp2Tk+OxHapWcYlFF72yQAfzCu37WjdM0K8P9gnfoAAAAAAAQNDZgomNGzdWfHx8pYJTqBpWq1X5+fnKzMyUJDVt2rTCfQU9oPjcc88pMjJS99xzj9/n9OzZU1OnTlW7du20b98+jRs3Tr1799b69es9lqoeP368xo0bF8xhIwjyCkvswcR2TZK0af9R7TqUH+ZRAQAAAACAYCopKbEHExs0aBDu4SAAcXFxkqTMzEw1bty4wtOfg1rleeXKlXrllVc0derUgCLTgwYN0lVXXaVOnTpp4MCB+v7773XkyBF99tlnHtuPGTNG2dnZ9q9du3YF6yGgEopKLPb7H9/eU5JUbLHKYrGGa0gAAAAAACDIbGsmxsfHh3kkqAjbz60ya18GNaC4YMECZWZmqkWLFoqMjFRkZKR27NihBx54QK1atfK7n9TUVLVt21Z//fWXx+MxMTFKTk52+UL42QKKUWaToiIdv1pFFou3UwAAAAAAQA3FNOeaKRg/t6BOeb7xxhs1YMAAl30DBw7UjTfeqJtvvtnvfnJzc7V161bdeOONwRweQqyo2MhEjDJHKNrsFFAssSqGot0AAAAAAAC1QsBhntzcXJfMwW3btmn16tWqX7++WrRoUWbufFRUlNLS0tSuXTv7vv79+2vo0KEaOXKkJOnBBx/U4MGD1bJlS+3du1dPPPGEzGazrr322oo+LoRBoT1DMUJRzgHFYotEoWcAAAAAAIBaIeApzytWrFCXLl3UpUsXSdLo0aPVpUsXPf744373sXXrVmVlZdm3d+/erWuvvVbt2rXT1VdfrQYNGmjJkiVq1KhRoMNDGBVbHAFFc4RJEaUZtM5rKwIAAAAAAFRH27dvl8lk0urVq8M9lGov4AzFPn36yGr1v8jG9u3by903bdq0QIeBKnCssET3f7pae44c8699UYkkKdpsRBKjzBEqKLZo+HvLXDIWK+KskxtozKAOlerD2Us/btLcTQeC1l8wnHlifT1y8SnhHgYAAAAAAHVS8+bNtW/fPjVs2LDCfYwdO1YzZsyo9UFJVraDV8u3H9KsPzICPq9ZfaNaUIv68dqSmas/M45Weizr9mRrVL82SgzCYoyFxRa99ovngj/htG5Ptkb2baOU+KhwDwUAAAAAgDrHbDYrLS3N63Gr1aqSkhJFRhJO4zsArwqKjanKJzVK0KOX+Jc5Z5LUtWU9SdJnd/bS6t1HKjcIq3Tz1OWSjEBgMNZidJ6C/faN3RQdGdRi5xVyy9TlslqlgpISSQQUAQAAAAA1h9Vqtc9arGpxUeaAqhbPmjVLTz31lNavXy+z2axevXrplVde0UknnaTt27erdevW+v3333X66adr7ty56tu3r77//ns9+uijWrdunX788Uf16dPHY99Tp07VuHHjJDkqKU+ZMkU33XSTjhw5ogcffFBff/21CgoK1L17d02YMEGdO3eW5MhsvOeeezR27FgdOnRIw4cP12uvvaaXXnpJL7/8siwWi+6991498sgj9muaTCa98cYb+uabbzR37lw1bdpUzz//vK688soKfkf9Q0ARXtkCbw0SY9S3XeOAz6+XEF2h89xFRphUbLEGbS1G534GdGgic0T4y9xHl04PLyrxfzkBAAAAAACqg2NFJTrl8dlhufaGJwcqPtr/8FZeXp5Gjx6tTp06KTc3V48//riGDh3qc4ryv/71L7344os68cQTVa9ePa/thg0bpvXr12vWrFn6+eefJUkpKSmSpKuuukpxcXH64YcflJKSorffflv9+/fX5s2bVb9+fUlGzZEffvhBs2bN0tatW3XllVfq77//Vtu2bTVv3jz99ttvuuWWWzRgwAD17NnTft3HHntMzz77rF555RV9+OGHuuaaa7Ru3Tp16BC8pePcEVCEV0X2qs3hDbhFmo2AYmFxcAKKtmrUJpOqRTBRcqw3WRSkxwgAAAAAAMq64oorXLbfe+89NWrUSBs2bFBiYqLHc5588kmdf/755fYdFxenxMRERUZGukydXrhwoZYtW6bMzEzFxBhTL1988UXNmDFDX3zxhe644w5JksVi0XvvvaekpCSdcsop6tu3rzZt2qTvv/9eERERateunZ577jn9+uuvLgHFq666Srfddpsk6T//+Y9++uknvfbaa3rjjTcC++YEgIAivLIF8CpbUKWyoswROl5kCWKGotXeb3VhC9pSERsAAAAAUNPERZm14cmBYbt2ILZs2aLHH39cS5cuVVZWliwW4334zp07dcopnpd76969e6XGuGbNGuXm5qpBgwYu+48dO6atW7fat1u1aqWkpCT7dpMmTWQ2mxUREeGyLzMz06WfXr16ldkOdVEYAorwqroE3qJLrx+s6cC2LMDoahVQNMZSSEARAAAAAFDDmEymgKYdh9PgwYPVsmVLvfvuu0pPT5fFYtGpp56qwsJCr+ckJCRU6pq5ublq2rSp5s6dW+ZYamqq/X5UlGtNBZPJ5HGfLQgaTjXjp+2nnzfuV0JifrntWjdMUNsmSeW2C5f8wmIt+ftg2NfTW7cnW1L4A2+2YNv8zQe0/WBepfvLyD5e2m/1mO4sOR7jwi1Z2n34WJnjbRon6sRG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nniixo0bp+LiYq/9PPzww2rbtq3i4+N14okn6rHHHrNXF586darGjRunNWvWyGQyyWQyaerUqfbr2zLxzjrrLD388MMu/R44cEBRUVGaP3++398zT+bOnaubb75Z2dnZ9jGMHTtWknT48GENHz5c9erVU3x8vAYNGqQtW7b49f2z/d7NmDHD/rswcOBA7dq1y+s5y5cv1/nnn6+GDRsqJSVF5513nlatWmU/3qpVK0nS0KFDZTKZ1KpVK02dOlWnnXaaJOnEE0+UyWTS9u3bJUlvvvmmTjrpJEVHR6tdu3b68MMPXa7n6Wds+/t477331KJFCyUmJuof//iHSkpK9PzzzystLU2NGze2/z5UxBNPPKGmTZtq7dq19sf1zDPP6JZbblFSUpJatGihd955x+WcdevWqV+/foqLi1ODBg10xx13KDc3V5LxN/3+++/r66+/tv8M586dq8LCQo0cOVJNmzZVbGysWrZsqfHjx1d43AAAAABQ51HM0i91M6BotUqFeeH5cgqq+dK7d28dPXpUv//+uyRp3rx5atiwoUsAad68eerTp4/H87du3aoZM2Zo5syZmjlzpubNm6dnn33Wfvyhhx7SvHnz9PXXX+vHH3/U3LlzXQI7vqxYsUL33HOPnnzySW3atEmzZs3Sueee69Lm/fffV2RkpJYtW6ZXXnlFL7/8sv773//aj48cOVKLFy/WtGnTtHbtWl111VW68MIL7cGsrVu36sILL9QVV1yhtWvX6tNPP9XChQs1cuRIex833XSTdu3apV9//VVffPGF3njjDWVmZvr1GGzGjh2roUOHat26dbrlllu0YMECDR8+XPfee682bNigt99+W1OnTvUZXEpKStLUqVO1YcMGvfLKK3r33Xc1YcIEScZ09AceeEAdO3bUvn37tG/fPg0bNqxMH9dff72mTZvmEnT99NNPlZ6ert69e/v1PfPmrLPO0sSJE5WcnGwfw4MPPmj/Hq5YsULffPONFi9eLKvVqosuusgeEC1Pfn6+nn76aX3wwQdatGiRjhw5omuuucZr+6NHj2rEiBFauHChlixZojZt2uiiiy6yB7uXL18uSZoyZYr27dun5cuXa9iwYfr5558lScuWLdO+ffvUvHlzTZ8+Xffee68eeOABrV+/Xnfeeaduvvlm/frrry7XdP8ZS8bv1w8//KBZs2bpk08+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/v0de7BdeumlGjx4sK677jqZzWafa0m669Chg6ZOnaq8vDx7AHTRokWKiIiwZw57+z1KTk7WsGHDNGzYMF155ZW68MILdejQIdWvXz84DwwAAAAA6hpr+e9J6zqmPFdj9erVU6dOnfTRRx/Zi6+ce+65WrVqVUDBP3eJiYm69dZb9dBDD+mXX37R+vXrddNNN7lM9/Rl5syZevXVV7V69Wrt2LFDH3zwgSwWi8uU6Z07d2r06NHatGmTPvnkE7322mu69957JUlt27bV9ddfr+HDh+urr77Stm3btGzZMo0fP17fffedJKNy8m+//aaRI0dq9erV2rJli77++mt7kYp27drpwgsv1J133qmlS5dq5cqVuu222xQXF1eh74nN448/rg8++EDjxo3TH3/8oY0bN2ratGl69NFHPbZv06aNdu7cqWnTpmnr1q169dVXNX36dJc2rVq10rZt27R69WplZWWpoKDAY18JCQkaMmSIHnvsMW3cuFHXXnut/Zg/3zNfWrVqpdzcXM2ZM0dZWVnKz89XmzZtdNlll+n222/XwoULtWbNGt1www064YQTdNlll/n1/YqKitKoUaPsP4ObbrpJZ555pj3A6On79eGHH2rjxo1aunSprr/++jI/s1atWmnOnDnKyMjQ4cOHvV77oYce0tSpU/Xmm29qy5Ytevnll/XVV1/ZC86Ew9ChQ/Xhhx/q5ptvDihL8Prrr1dsbKxGjBih9evX69dff9WoUaN04403qkmTJpKM78vatWu1adMmZWVlqaioSC+//LI++eQT/fnnn9q8ebM+//xzpaWlBTz1HwAAAADqLE/ZiBYCiuUhoFjNnXfeeSopKbEHFOvXr69TTjlFaWlpfq956MkLL7yg3r17a/DgwRowYIDOOeccl/UIfUlNTdVXX32lfv36qUOHDnrrrbf0ySefqGPHjvY2w4cP17Fjx3TGGWfo7rvv1r333qs77rjDfnzKlCkaPny4HnjgAbVr105DhgzR8uXL7RmTnTp10rx587R582b17t1bXbp00eOPP6709HSXPtLT03Xeeefp8ssv1x133KHGjRtX+HsiSQMHDtTMmTP1448/qkePHjrzzDM1YcIEtWzZ0mP7Sy+9VPfff79Gjhyp008/Xb/99psee+wxlzZXXHGFLrzwQvXt21eNGjXSJ5984vX6119/vdasWaPe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zZrl9Jzp06crLi5OOTnmH8iePXt05ZVXKjExUUlJSbr00ku1a9cu+/q21/Hss8+qSZMmat++fbnjOnDggIYOHaro6Gi1bNlSn332mVJTU50CfhaLRW+++aYuueQS1alTR88++6wmTpyoHj166OOPP1ZqaqoSEhJ01VVX6dixkrbzlf2dAQAAAADc2565Xa8sf0VH8o4oxBKijkkd1Tm5s27tdqtiwmPsP6UDKlFhUU6P++MnPDRcYSFhalinoW7scqM6JnV0+TO+x3i/77uiPzYWi8Upgy00JNRv+3AXTLTt17YewcSKKe/376/GQ+Eh4RrQbIDOTTnX7Xv7tu63qW5UXbdzLHpSK0ueHSPq1S06LNrrzsAHDhzQ1VdfrRdeeEEjR47UsWPHtHDhQtmSSufMmaOoqCjNmzdPu3bt0rhx45ScnKxnn31WkvTggw9q/vz5mjFjhho0aKBHH31UK1euVI8ePbza/w033KCtW7dq5syZio+P18MPP6yLL75YGzZsUHx8vIYNG6bPPvtMF110kf05n376qUaMGKGYmBgVFhZqyJAh6tevnxYuXKiwsDA988wzuvDCC7V27Vp7JuKcOXMUHx+vn3/+2atxXX/99crIyNC8efMUHh6u++67T2lpaWXWmzhxop5//nlNnjxZYWFh+uCDD7R9+3Z9++23+v7773X06FFdeeWVev755/32OwMAAAAAuDfpj0laesAsI06JS9FXw78K8IhM9/a8V/f2vDfQwwD8KiY8Rq+d91r5K1ZArQwo5hblqu9nfQOy76XXLHW6quDJgQMHVFRUpMsuu0wtWrSQJHXt2tX+eEREhD744APFxMSoc+fOeuqpp/Tggw/q6aefVk5Ojt5//3198sknOv/88yVJH330kZo1a+bVvm2BxEWLFunMM8+UZAYLU1JS9O233+qKK67QmDFjdN111yknJ0cxMTHKzs7WDz/8oOnTp0uSvvzyS1mtVr333nv2IOqUKVOUmJioefPmafDgwZKkOnXq6L333vOq1HnTpk365ZdftGzZMvXqZc6r8N5776lt27Zl1r3mmms0btw4p2VWq1Uffvih4uLiJEnXXXed5syZo2effVbHjx+v1O8MAAAAAODe/3b9zx5MHN5quK5sf2WARwSgoih5DmLdu3fX+eefr65du+qKK67Qu+++q6NHjzo9HhNTEpzs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7f3222/q37+/kpOTFR0drQ4dOuhf//qXT/sFgMpwd3w5cuSI7rzzTrVv317R0dFq3ry57rrrLmVlZXncHudrAALN03nT3/72N7Vu3VrR0dGqX7++Lr30Um3atMnj9jiuIdAIKKJW+fLLL3XfffdpwoQJWrlypbp3764hQ4YoLS1NknTvvffqu+++09dff6358+dr//79uuyyyzxuMykpSf/3f/+nJUuWaO3atRo3bpzGjRunn376yb7OCy+8oNdee01vvfWWli5dqjp16mjIkCHKy8ur0tcLoOYr77iWk5OjCy+8UI8++qjX26xTp47uuOMOLViwQBs3btRjjz2mxx57TO+8847X+wWAivJ0fNm/f7/279+vl156SevXr9eHH36o2bNn66abbvK4Tc7XAARSeedNPXv21JQpU7Rx40b99NNPMgxDgwcPVnFxsdttclxDwBlALdKnTx9j/Pjx9vvFxcVGkyZNjEmTJhmZmZlGeHi48fXXX9sf37hxoyHJWLJkiU/7Oe2004zHHnvMMAzDsFqtRqNGjYwXX3zR/nhmZqYRGRlpfP7555V8RQBqO0/HNUe//vqrIck4evRohfYzcuRI49prr/V5vwDgK1+PL1999ZURERFhFBYW+rQfztcAVBdfj2tr1qwxJBnbtm3zaT8c11CdgiZD0d9lqJL09ddfq0OHDoqKilLXrl31448/Oj1ukP5bqxQUFGjFihUaNGiQfVlISIgGDRqkJUuWaMWKFSosLHR6vEOHDmrevLmWLFliX5aamqqJEye63IdhGJozZ442b96sAQMGSJJ27typgwcPOm03ISFBffv2ddouah5/l6FK0rx583T66acrMjJSbdq00YcffujTflGzlHdc89bAgQN1ww03uH181apVWrx4sc455xy/7henHn+XoUqcr8FZRY4vWVlZio+PV1hYmH0Z52vwlr/LUCWOa3Dm63HtxIkTmjJlilq2bKmUlBT7co5rCDZBEVCsijLUxYsX6+qrr9ZNN92kVatWacSIERoxYoTWr19vX4f039olIyNDxcXFatiwodPyhg0b6uDBgzp48KAiIiKUmJjo8nGb1q1bq169ek7rZGVlKTY2VhERERo6dKhef/11XXDBBZJkf667/aJmqooy1J07d2ro0KE699xztXr1at1zzz26+eabncoaKEOtXco7rnmrefPm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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Plot data\n", - "ds_resampled[\n", - " [\n", - " \"air_temp\",\n", - " \"wind_speed_relative_to_platform_knots\",\n", - " \"spd_over_grnd\",\n", - " ]\n", - "].to_dataframe().plot(xlabel=\"time\", figsize=(16, 8), subplots=True)\n" + "data": { + "text/plain": [ + "array([, ,\n", + " ], dtype=object)" ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" }, { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Plot position on a map" + "data": { + "image/png": "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uHHjJDkqKU+ZMkU33XSTjhw5ogcffFBff/21CgoK1L17d02YMEGdO3eW5MhsvOeeezR27FgdOnRIw4cP12uvvaaXXnpJL7/8siwWi+6991498sgj9muaTCa98cYb+uabbzR37lw1bdpUzz//vK688soKfkf9Q0ARXtkCbw0SY9S3XeOAz6+XEF2h89xFRphUbLEGbS1G534GdGgic0T4y9xHl04PLyrxfzkBAAAAAACqg2NFJTrl8dlhufaGJwcqPtr/8FZeXp5Gjx6tTp06KTc3V48//riGDh3qc4ryv/71L7344os68cQTVa9ePa/thg0bpvXr12vWrFn6+eefJUkpKSmSpKuuukpxcXH64YcflJKSorffflv9+/fX5s2bVb9+fUlGzZEffvhBs2bN0tatW3XllVfq77//Vtu2bTVv3jz99ttvuuWWWzRgwAD17NnTft3HHntMzz77rF555RV9+OGHuuaaa7Ru3Tp16BC8pePcEVCEV0X2qs3hDbhFmo2AYmFxcAKKtmrUJpOqRTBRcqw3WRSkxwgAAAAAAMq64oorXLbfe+89NWrUSBs2bFBiYqLHc5588kmdf/755fYdFxenxMRERUZGukydXrhwoZYtW6bMzEzFxBhTL1988UXNmDFDX3zxhe644w5JksVi0XvvvaekpCSdcsop6tu3rzZt2qTvv/9eERERateunZ577jn9+uuvLgHFq666Srfddpsk6T//+Y9++uknvfbaa3rjjTcC++YEgIAivLIF8CpbUKWyoswROl5kCWKGotXeb3VhC9pSERsAAAAAUNPERZm14cmBYbt2ILZs2aLHH39cS5cuVVZWliwW4334zp07dcopnpd76969e6XGuGbNGuXm5qpBgwYu+48dO6atW7fat1u1aqWkpCT7dpMmTWQ2mxUREeGyLzMz06WfXr16ldkOdVEYAorwqroE3qJLrx+s6cC2LMDoahVQNMZSSEARAAAAAFDDmEymgKYdh9PgwYPVsmVLvfvuu0pPT5fFYtGpp56qwsJCr+ckJCRU6pq5ublq2rSp5s6dW+ZYamqq/X5UlGtNBZPJ5HGfLQgaTjXjp+2nnzfuV0JifrntWjdMUNsmSeW2C5f8wmIt+ftg2NfTW7cnW1L4A2+2YNv8zQe0/WBepfvLyD5e2m/1mO4sOR7jwi1Z2n34WJnjbRon6sRG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nniixo0bp+LiYq/9PPzww2rbtq3i4+N14okn6rHHHrNXF586darGjRunNWvWyGQyyWQyaerUqfbr2zLxzjrrLD388MMu/R44cEBRUVGaP3++398zT+bOnaubb75Z2dnZ9jGMHTtWknT48GENHz5c9erVU3x8vAYNGqQtW7b49f2z/d7NmDHD/rswcOBA7dq1y+s5y5cv1/nnn6+GDRsqJSVF5513nlatWmU/3qpVK0nS0KFDZTKZ1KpVK02dOlWnnXaaJOnEE0+UyWTS9u3bJUlvvvmmTjrpJEVHR6tdu3b68MMPXa7n6Wds+/t477331KJFCyUmJuof//iHSkpK9PzzzystLU2NGze2/z5UxBNPPKGmTZtq7dq19sf1zDPP6JZbblFSUpJatGihd955x+WcdevWqV+/foqLi1ODBg10xx13KDc3V5LxN/3+++/r66+/tv8M586dq8LCQo0cOVJNmzZVbGysWrZsqfHjx1d43AAAAABQ51HM0i91M6BotUqFeeH5cgqq+dK7d28dPXpUv//+uyRp3rx5atiwoUsAad68eerTp4/H87du3aoZM2Zo5syZmjlzpubNm6dnn33Wfvyhhx7SvHnz9PXXX+vHH3/U3LlzXQI7vqxYsUL33HOPnnzySW3atEmzZs3Sueee69Lm/fffV2RkpJYtW6ZXXnlFL7/8sv773//aj48cOVKLFy/WtGnTtHbtWl111VW68MIL7cGsrVu36sILL9QVV1yhtWvX6tNPP9XChQs1cuRIex833XSTdu3apV9//VVffPGF3njjDWVmZvr1GGzGjh2roUOHat26dbrlllu0YMECDR8+XPfee682bNigt99+W1OnTvUZXEpKStLUqVO1YcMGvfLKK3r33Xc1YcIEScZ09AceeEAdO3bUvn37tG/fPg0bNqxMH9dff72mTZvmEnT99NNPlZ6ert69e/v1PfPmrLPO0sSJE5WcnGwfw4MPPmj/Hq5YsULffPONFi9eLKvVqosuusgeEC1Pfn6+nn76aX3wwQdatGiRjhw5omuuucZr+6NHj2rEiBFauHChlixZojZt2uiiiy6yB7uXL18uSZoyZYr27dun5cuXa9iwYfr5558lScuWLdO+ffvUvHlzTZ8+Xffee68eeOABrV+/Xnfeeaduvvlm/frrry7XdP8ZS8bv1w8//KBZs2bpk08+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/v0de7BdeumlGjx4sK677jqZzWafa0m669Chg6ZOnaq8vDx7AHTRokWKiIiwZw57+z1KTk7WsGHDNGzYMF155ZW68MILdejQIdWvXz84DwwAAAAA6hpr+e9J6zqmPFdj9erVU6dOnfTRRx/Zi6+ce+65WrVqVUDBP3eJiYm69dZb9dBDD+mXX37R+vXrddNNN7lM9/Rl5syZevXVV7V69Wrt2LFDH3zwgSwWi8uU6Z07d2r06NHatGmTPvnkE7322mu69957JUlt27bV9ddfr+HDh+urr77Stm3btGzZMo0fP17fffedJKNy8m+//aaRI0dq9erV2rJli77++mt7kYp27drpwgsv1J133qmlS5dq5cqVuu222xQXF1eh74nN448/rg8++EDjxo3TH3/8oY0bN2ratGl69NFHPbZv06aNdu7cqWnTpmnr1q169dVXNX36dJc2rVq10rZt27R69WplZWWpoKDAY18JCQkaMmSIHnvsMW3cuFHXXnut/Zg/3zNfWrVqpdzcXM2ZM0dZWVnKz89XmzZtdNlll+n222/XwoULtWbNGt1www064YQTdNlll/n1/YqKitKoUaPsP4ObbrpJZ555pj3A6On79eGHH2rjxo1aunSprr/++jI/s1atWmnOnDnKyMjQ4cOHvV77oYce0tSpU/Xmm29qy5Ytevnll/XVV1/ZC86Ew9ChQ/Xhhx/q5ptvDihL8Prrr1dsbKxGjBih9evX69dff9WoUaN04403qkmTJpKM78vatWu1adMmZWVlqaioSC+//LI++eQT/fnnn9q8ebM+//xzpaWlBTz1HwAAAADqLE/ZiBYCiuUhoFjNnXfeeSopKbEHFOvXr69TTjlFaWlpfq956MkLL7yg3r17a/DgwRowYIDOOeccl/UIfUlNTdVXX32lfv36qUOHDnrrrbf0ySefqGPHjvY2w4cP17Fjx3TGGWfo7rvv1r333qs77rjDfnzKlCkaPny4HnjgAbVr105DhgzR8uXL7RmTnTp10rx587R582b17t1bXbp00eOPP6709HSXPtLT03Xeeefp8ssv1x133KHGjRtX+HsiSQMHDtTMmTP1448/qkePHjrzzDM1YcIEtWzZ0mP7Sy+9VPfff79Gjhyp008/Xb/99psee+wxlzZXXHGFLrzwQvXt21eNGjXSJ5984vX6119/vdasWaPe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zZrl9Jzp06crLi5OOTnmH8iePXt05ZVXKjExUUlJSbr00ku1a9cu+/q21/Hss8+qSZMmat++fbnjOnDggIYOHaro6Gi1bNlSn332mVJTU50CfhaLRW+++aYuueQS1alTR88++6wmTpyoHj166OOPP1ZqaqoSEhJ01VVX6dixkrbzlf2dAQAAAADc2565Xa8sf0VH8o4oxBKijkkd1Tm5s27tdqtiwmPsP6UDKlFhUU6P++MnPDRcYSFhalinoW7scqM6JnV0+TO+x3i/77uiPzYWi8Upgy00JNRv+3AXTLTt17YewcSKKe/376/GQ+Eh4RrQbIDOTTnX7Xv7tu63qW5UXbdzLHpSK0ueHSPq1S06LNrrzsAHDhzQ1VdfrRdeeEEjR47UsWPHtHDhQtmSSufMmaOoqCjNmzdPu3bt0rhx45ScnKxnn31WkvTggw9q/vz5mjFjhho0aKBHH31UK1euVI8ePbza/w033KCtW7dq5syZio+P18MPP6yLL75YGzZsUHx8vIYNG6bPPvtMF110kf05n376qUaMGKGYmBgVFhZqyJAh6tevnxYuXKiwsDA988wzuvDCC7V27Vp7JuKcOXMUHx+vn3/+2atxXX/99crIyNC8efMUHh6u++67T2lpaWXWmzhxop5//nlNnjxZYWFh+uCDD7R9+3Z9++23+v7773X06FFdeeWVev755/32OwMAAAAAuDfpj0laesAsI06JS9FXw78K8IhM9/a8V/f2vDfQwwD8KiY8Rq+d91r5K1ZArQwo5hblqu9nfQOy76XXLHW6quDJgQMHVFRUpMsuu0wtWrSQJHXt2tX+eEREhD744APFxMSoc+fOeuqpp/Tggw/q6aefVk5Ojt5//3198sknOv/88yVJH330kZo1a+bVvm2BxEWLFunMM8+UZAYLU1JS9O233+qKK67QmDFjdN111yknJ0cxMTHKzs7WDz/8oOnTp0uSvvzyS1mtVr333nv2IOqUKVOUmJioefPmafDgwZKkOnXq6L333vOq1HnTpk365ZdftGzZMvXqZc6r8N5776lt27Zl1r3mmms0btw4p2VWq1Uffvih4uLiJEnXXXed5syZo2effVbHjx+v1O8MAAAAAODe/3b9zx5MHN5quK5sf2WARwSgoih5DmLdu3fX+eefr65du+qKK67Qu+++q6NHjzo9HhNTEpzs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7f3222/q37+/kpOTFR0drQ4dOuhf//qXT/sFgMpwd3w5cuSI7rzzTrVv317R0dFq3ry57rrrLmVlZXncHudrAALN03nT3/72N7Vu3VrR0dGqX7++Lr30Um3atMnj9jiuIdAIKKJW+fLLL3XfffdpwoQJWrlypbp3764hQ4YoLS1NknTvvffqu+++09dff6358+dr//79uuyyyzxuMykpSf/3f/+nJUuWaO3atRo3bpzGjRunn376yb7OCy+8oNdee01vvfWWli5dqjp16mjIkCHKy8ur0tcLoOYr77iWk5OjCy+8UI8++qjX26xTp47uuOMOLViwQBs3btRjjz2mxx57TO+8847X+wWAivJ0fNm/f7/279+vl156SevXr9eHH36o2bNn66abbvK4Tc7XAARSeedNPXv21JQpU7Rx40b99NNPMgxDgwcPVnFxsdttclxDwBlALdKnTx9j/Pjx9vvFxcVGkyZNjEmTJhmZmZlGeHi48fXXX9sf37hxoyHJWLJkiU/7Oe2004zHHnvMMAzDsFqtRqNGjYwXX3zR/nhmZqYRGRlpfP7555V8RQBqO0/HNUe//vqrIck4evRohfYzcuRI49prr/V5vwDgK1+PL1999ZURERFhFBYW+rQfztcAVBdfj2tr1qwxJBnbtm3zaT8c11CdgiZD0d9lqJL09ddfq0OHDoqKilLXrl31448/Oj1ukP5bqxQUFGjFihUaNGiQfVlISIgGDRqkJUuWaMWKFSosLHR6vEOHDmrevLmWLFliX5aamqqJEye63IdhGJozZ442b96sAQMGSJJ27typgwcPOm03ISFBffv2ddouah5/l6FK0rx583T66acrMjJSbdq00YcffujTflGzlHdc89bAgQN1ww03uH181apVWrx4sc455xy/7henHn+XoUqcr8FZRY4vWVlZio+PV1hYmH0Z52vwlr/LUCWOa3Dm63HtxIkTmjJlilq2bKmUlBT7co5rCDZBEVCsijLUxYsX6+qrr9ZNN92kVatWacSIERoxYoTWr19vX4f039olIyNDxcXFatiwodPyhg0b6uDBgzp48KAiIiKUmJjo8nGb1q1bq169ek7rZGVlKTY2VhERERo6dKhef/11XXDBBZJkf667/aJmqooy1J07d2ro0KE699xztXr1at1zzz26+eabncoaKEOtXco7rnmrefPm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", + "text/plain": [ + "
" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot data\n", + "ds_resampled[\n", + " [\n", + " \"air_temp\",\n", + " \"wind_speed_relative_to_platform_knots\",\n", + " \"spd_over_grnd\",\n", + " ]\n", + "].to_dataframe().plot(xlabel=\"time\", figsize=(16, 8), subplots=True)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot position on a map" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 101, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
Make this Notebook Trusted to load map: File -> Trust Notebook
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 101, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], - "source": [ - "# Plot lat/lon on a map\n", - "lat, lon = \"latitude_degrees_north\", \"longitude_degrees_east\"\n", - "df = ds_resampled.to_dataframe()\n", - "map = folium.Map(location=df[[lat, lon]].mean())\n", - "folium.PolyLine(df[[lat, lon]].dropna().values, color=\"red\").add_to(map)\n", - "map\n" + "text/plain": [ + "" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "Untitled26.ipynb", - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" } + ], + "source": [ + "# Plot lat/lon on a map\n", + "lat, lon = \"latitude_degrees_north\", \"longitude_degrees_east\"\n", + "df = ds_resampled.to_dataframe()\n", + "map = folium.Map(location=df[[lat, lon]].mean())\n", + "folium.PolyLine(df[[lat, lon]].dropna().values, color=\"red\").add_to(map)\n", + "map" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "Untitled26.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/ocean_data_parser/__init__.py b/ocean_data_parser/__init__.py index 0230a6d6..c485925f 100644 --- a/ocean_data_parser/__init__.py +++ b/ocean_data_parser/__init__.py @@ -1,6 +1,5 @@ import re from pathlib import Path -from ocean_data_parser._version import __version__ - PARSERS = re.findall('parser = "(.*)"', (Path(__file__).parent / "read.py").read_text()) +__version__ = "0.5.0" diff --git a/ocean_data_parser/_version.py b/ocean_data_parser/_version.py deleted file mode 100644 index 493f7415..00000000 --- a/ocean_data_parser/_version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.3.0" diff --git a/ocean_data_parser/batch/config.py b/ocean_data_parser/batch/config.py index 91111b6c..1cd7a9e1 100644 --- a/ocean_data_parser/batch/config.py +++ b/ocean_data_parser/batch/config.py @@ -105,6 +105,3 @@ def load_config(config_path: str = None, encoding="UTF-8"): ) return config - - -# config = load_config(DEFAULT_CONFIG_PATH) diff --git a/ocean_data_parser/batch/convert.py b/ocean_data_parser/batch/convert.py index ff496134..6cd7f7df 100644 --- a/ocean_data_parser/batch/convert.py +++ b/ocean_data_parser/batch/convert.py @@ -4,6 +4,7 @@ from glob import glob from multiprocessing import Pool from pathlib import Path +import json import click import pandas as pd @@ -11,8 +12,7 @@ from tqdm import tqdm from xarray import Dataset -from ocean_data_parser import PARSERS, geo, process, read -from ocean_data_parser._version import __version__ +from ocean_data_parser import PARSERS, __version__, geo, read from ocean_data_parser.batch.config import load_config from ocean_data_parser.batch.registry import FileConversionRegistry from ocean_data_parser.batch.utils import VariableLevelLogger, generate_output_path @@ -69,8 +69,20 @@ def get_parser_list(ctx, _, value): click.echo(get_parser_list_string()) ctx.exit() +def validate_parser_kwargs(ctx, _, value): + """Test if given parser_kwargs is a valid JSON string and return the parsed JSON object""" + if not value: + return value + try: + return json.loads(value) + except json.JSONDecodeError: + raise click.BadParameter( + click.style( + "parser-kwargs should be a valid JSON string", fg="bright_red" + ) + ) -@click.command(context_settings={"auto_envvar_prefix": "ODPY_CONVERT"}) +@click.command(name="convert", context_settings={"auto_envvar_prefix": "ODPY_CONVERT"}) @click.option( "-i", "--input-path", @@ -84,6 +96,7 @@ def get_parser_list(ctx, _, value): ) @click.option( "--parser", + "-p", type=str, help=( "Parser used to parse the data. Default to auto detectection." @@ -91,9 +104,21 @@ def get_parser_list(ctx, _, value): ), callback=validate_parser, ) +@click.option( + "--parser-kwargs", + type=str, + help=( + "Parser key word arguments to pass to the parser. Expect a JSON string." + " (ex: '{\"globa_attributes\": {\"project\": \"test\"}')" + ), + default=None, + callback=validate_parser_kwargs, +) @click.option( "--overwrite", type=bool, + is_flag=True, + default=False, help="Overwrite already converted files when source file is changed.", ) @click.option( @@ -120,6 +145,7 @@ def get_parser_list(ctx, _, value): ) @click.option( "--output-path", + "-o", type=click.Path(), help="Output directory where to save converted files.", ) @@ -157,29 +183,28 @@ def get_parser_list(ctx, _, value): help="Print present argument values. If stop argument is given, do not run the conversion.", ) @click.version_option(version=__version__, package_name="ocean-data-parser.convert") -def convert(**kwargs): - """Run ocean-data-parser conversion on given files.""" +def cli(**kwargs): + """Ocean Data Parser Batch Conversion CLI Interface.""" # Drop empty kwargs if kwargs.get("show_arguments"): click.echo("odpy convert parameter inputs:") click.echo("\n".join([f"{key}={value}" for key, value in kwargs.items()])) if kwargs["show_arguments"] == "stop": return - kwargs.pop("show_arguments", None) + kwargs.pop("show_arguments") + kwargs.pop("new_config") + convert(**kwargs) - kwargs = { - key: None if value == "None" else value - for key, value in kwargs.items() - if value - } +def convert(**kwargs): + """Run ocean-data-parser conversion on given files.""" BatchConversion(**kwargs).run() class BatchConversion: def __init__(self, config=None, **kwargs): self.config = self._get_config(config, **kwargs) - self.registry = FileConversionRegistry(**self.config["registry"]) + self.registry = FileConversionRegistry(**self.config.get("registry", {})) @staticmethod def _get_config(config: dict = None, **kwargs) -> dict: @@ -191,6 +216,10 @@ def _get_config(config: dict = None, **kwargs) -> dict: Returns: dict: combined configuration """ + if config: + logger.info("Load configuration file and ignore other inputs") + return load_config(config) if isinstance(config, str) else config or {} + logger.info("Load configuration={}, kwargs={}", config, kwargs) output_kwarg = { key[7:]: kwargs.pop(key) @@ -204,11 +233,11 @@ def _get_config(config: dict = None, **kwargs) -> dict: } config = { **load_config(DEFAULT_CONFIG_PATH), - **(load_config(config) if isinstance(config, str) else config or {}), **kwargs, } config["output"].update(output_kwarg) config["registry"].update(registry_kwarg) + return config def get_excluded_files(self) -> list: @@ -221,7 +250,7 @@ def get_excluded_files(self) -> list: def get_source_files(self) -> list: excluded_files = self.get_excluded_files() return [ - file + Path(file) for file in glob(self.config["input_path"], recursive=True) if file not in excluded_files ] @@ -235,7 +264,7 @@ def _get_parser(self): def _convert(self, files: list) -> list: # Load parser and generate inputs to conversion scripts parser = self._get_parser() - inputs = ((file, parser, self.config) for file in files) + inputs = ((str(file), parser, self.config) for file in files) tqdm_parameters = dict(unit="file", total=len(files)) # single pool processing @@ -260,7 +289,11 @@ def _convert(self, files: list) -> list: def run(self): """Run Batch conversion""" - logger.info("Run ocean-data-parser[{}] batch conversion", __version__) + logger.info( + "Run ocean-data-parser[{}] convert {}", + __version__, + self.config.get("name", ""), + ) files = self.get_source_files() if not files: error_message = f"ERROR No files detected with {self.config['input_path']}" @@ -268,7 +301,9 @@ def run(self): sys.exit(error_message) self.registry.add(files) - files = self.registry.get_modified_source_files() + files = self.registry.get_modified_source_files( + overwrite=self.config["overwrite"] + ) if not files: logger.info("No file to parse. Conversion completed") return self.registry @@ -284,9 +319,10 @@ def run(self): .set_index("sources") .replace({"": None}) ) + conversion_log.index = conversion_log.index.map(Path) self.registry.update_fields(files, dataframe=conversion_log) self.registry.save() - self.registry.summarize() + self.registry.summarize(sources=files, output=self.config.get("summary")) logger.info("Conversion completed") return self.registry @@ -355,7 +391,7 @@ def _get_mapped_global_attributes( # Parse file to xarray logger.debug("Parse file: {}", file) - ds = read.file(file, parser=parser) + ds = read.file(file, parser=parser, **config.get("parser_kwargs", {})) if not isinstance(ds, Dataset): raise RuntimeError( f"{parser.__module__}{parser.__name__}:{file} " @@ -370,7 +406,7 @@ def _get_mapped_global_attributes( "source": file, } ) - for var, attrs in config.get("variable_attributes").items(): + for var, attrs in config.get("variable_attributes", {}).items(): if var in ds: ds[var].attrs.update(attrs) @@ -383,7 +419,7 @@ def _get_mapped_global_attributes( (ds["longitude"], ds["latitude"]), config["geographical_areas"]["regions"] ) if ( - config.get("reference_stations").get("path") + config.get("reference_stations", {}).get("path") and "latitude" in ds and "longitude" in ds ): @@ -395,7 +431,7 @@ def _get_mapped_global_attributes( ) # Processing - for pipe in config["xarray_pipe"]: + for pipe in config.get("xarray_pipe", []): ds = ds.pipe(*pipe) # TODO add to history @@ -416,8 +452,8 @@ def _get_mapped_global_attributes( # Save to output_path = generate_output_path(ds, **config["output"]) if not output_path.parent.exists(): - logger.info("Create new directory: {}", output_path.parent) - output_path.parent.mkdir(parents=True) + logger.debug("Create new directory: {}", output_path.parent) + output_path.parent.mkdir(parents=True, exist_ok=True) logger.trace("Save to: {}", output_path) ds.to_netcdf(output_path) @@ -425,4 +461,4 @@ def _get_mapped_global_attributes( if __name__ == "__main__": - convert(auto_envvar_prefix="ODPY_CONVERT") + cli(auto_envvar_prefix="ODPY_CONVERT") diff --git a/ocean_data_parser/batch/default-batch-config.yaml b/ocean_data_parser/batch/default-batch-config.yaml index f784bf28..69873f5d 100644 --- a/ocean_data_parser/batch/default-batch-config.yaml +++ b/ocean_data_parser/batch/default-batch-config.yaml @@ -1,12 +1,15 @@ -# File input -input_path: "" # file or glob expression -exclude: null # glob expression of files to exclude +--- + +name: Batch Conversion Name +input_path: "" # file or glob expression +exclude: null # glob expression of files to exclude parser: null -overwrite: False -multiprocessing: 1 # n processes to run [int] or null for all -errors: "ignore" # raise|ignore +parser_kwargs: {} +overwrite: false +multiprocessing: 1 # n processes to run [int] or null for all +errors: "ignore" # raise|ignore registry: - path: null # file_registry(.csv | .parquet) + path: null # file_registry(.csv | .parquet) sentry: dsn: https://cd67597c647c4767adf0b3acf5d3f6a6@o56764.ingest.sentry.io/4505529390137344 @@ -17,12 +20,12 @@ sentry: # Attributes and Metadata variable_attributes: {} global_attributes: {} -file_specific_attributes_path: null # Path to csv file with one column called "file" +file_specific_attributes_path: null # Path to csv file with one column called "file" global_attribute_mapping: - path: null # Path to csv file (accept glob parameter for multiple files) - mapping: null # mapping dataframe - by: [] # global attributes list - log_level: WARNING # [null, WARNING,ERROR] level log when no mapping exist + path: null # Path to csv file (accept glob parameter for multiple files) + mapping: null # mapping dataframe + by: [] # global attributes list + log_level: WARNING # [null, WARNING,ERROR] level log when no mapping exist # Geospatial References reference_stations: @@ -45,4 +48,6 @@ output: file_name: null file_preffix: "" file_suffix: "" - output_format: .nc \ No newline at end of file + output_format: .nc + +summary: null # Path to save summary file (csv) diff --git a/ocean_data_parser/batch/registry.py b/ocean_data_parser/batch/registry.py index 27cfb8c7..3ffcddd5 100644 --- a/ocean_data_parser/batch/registry.py +++ b/ocean_data_parser/batch/registry.py @@ -1,7 +1,6 @@ import copy import hashlib import logging -import re from pathlib import Path from typing import Union @@ -16,11 +15,26 @@ ).set_index("source") +REGISTRY_DTYPE = { + "mtime": float, + "hash": str, + "error_message": str, + "output_path": str, +} + + +def generate_registry(sources=None): + return pd.DataFrame( + data={"source": sources}, + columns=list(REGISTRY_DTYPE.keys()) + ["source"], + ).set_index("source") + + class FileConversionRegistry: def __init__( self, path: str = None, - data: pd.DataFrame = EMPTY_FILE_REGISTRY, + data: pd.DataFrame = generate_registry(), hashtype: str = "sha256", block_size: int = 65536, ): @@ -34,22 +48,26 @@ def __init__( def load(self, overwrite=False): """Load file registry if available otherwise return an empty dataframe""" + + def _as_path(path): + return Path(path) if pd.notna(path) else path + if not self.data.empty and not overwrite: logger.warning( "Registry already contains data and won't reload from: %s", self.data ) return elif self.path is None or not self.path.exists(): - self.data = pd.DataFrame() + self.data = generate_registry() elif self.path.suffix == ".csv": - self.data = pd.read_csv(self.path) + self.data = pd.read_csv(self.path, index_col="source", dtype=REGISTRY_DTYPE) elif self.path.suffix == ".parquet": self.data = pd.read_parquet(self.path) else: raise TypeError("Unknown registry type") - if "source" in self.data: - self.data = self.data.set_index(["source"]) + self.data.index = self.data.index.map(Path) + self.data["output_path"] = self.data["output_path"].apply(_as_path) return self def save(self): @@ -119,19 +137,26 @@ def add(self, sources: list): sources = [source for source in sources if source not in self.data.index] if not sources: return - new_data = pd.DataFrame({"source": sources}) + new_data = generate_registry(sources) # Retrieve mtime and hash only if a registry is actually saved if self.path: logger.info("Get new files mtime") - new_data["mtime"] = new_data["source"].progress_apply(self._get_mtime) - logger.info("Get new files hash") - new_data["hash"] = new_data["source"].progress_apply(self._get_hash) + mtimes = new_data.index.to_series().progress_apply(self._get_mtime) + logger.info("Get new files hashes") + hashes = new_data.index.to_series().progress_apply(self._get_hash) + new_data = new_data.assign( + mtime=mtimes, + hash=hashes, + ) + self.data = ( - pd.concat( + new_data + if self.data.empty + else pd.concat( [ self.data, - new_data.set_index(["source"]), + new_data, ], ) .groupby(level=0) @@ -181,7 +206,7 @@ def update_fields( sources: list = None, placeholder=None, dataframe: Union[list, pd.DataFrame] = None, - **kwargs + **kwargs, ): """Update registry sources with given values @@ -252,7 +277,7 @@ def get_missing_sources(self) -> list: def summarize(self, sources=None, by="error_message", output=None): """Generate a summary of the file registry errors""" if sources: - data = self.data[self.data[sources]] + data = self.data.loc[sources] else: data = self.data succeed = len(data.query("error_message.isna()")) @@ -266,6 +291,6 @@ def summarize(self, sources=None, by="error_message", output=None): ) errors.columns = (" ".join(col) for col in errors.columns) if not errors.empty: - logger.info("The following errors were captured:\n%s", errors) + logger.error("The following errors were captured:\n%s", errors) if output: errors.to_csv(output) diff --git a/ocean_data_parser/batch/utils.py b/ocean_data_parser/batch/utils.py index f72b8285..c2fcf519 100644 --- a/ocean_data_parser/batch/utils.py +++ b/ocean_data_parser/batch/utils.py @@ -3,11 +3,43 @@ from pathlib import Path from typing import Union +import numpy as np import pandas as pd import xarray from loguru import logger +def get_path_generation_input(ds: xarray.Dataset, source_path: Path) -> dict: + """Get all variables to be used in the path generation.""" + format_variables = { + # All global attribtes + **{f"{key}": value for key, value in ds.attrs.items() if value}, + # All variable attributes + **{ + f"variable_{var}_{key}": value + for var in ds.variables + for key, value in ds[var].attrs.items() + }, + # Source path and stem + "source_file": source_path, + "source_path": source_path.parent, + "source_stem": source_path.stem, + "pd": pd, + } + + # Add time_min and time_max as pandas Timestamp + if "time" in ds and isinstance(ds["time"].values, np.ndarray): + format_variables["time_min"] = ds["time"].to_index().min() + format_variables["time_max"] = ds["time"].to_index().max() + elif "time" in ds and isinstance(ds["time"].values, np.datetime64): + format_variables["time_min"] = pd.to_datetime(ds["time"].values) + format_variables["time_max"] = pd.to_datetime(ds["time"].values) + elif "time" in ds: + raise RuntimeError("Time variable is not compatible with Timestamp formating.") + + return format_variables + + def generate_output_path( ds: xarray.Dataset, path: Union[str, Path] = None, @@ -27,9 +59,11 @@ def generate_output_path( attributes accoding to the convention: - source_path: pathlib.Path of original parsed file filename - source_stem: original parsed file filename without the extension - - global attributes: `{global_asttribute}` + - global attributes: `{global_attribute}` - variable attributes: `{variable_[variable]_[attribute]}` - ex: ".\\{program}\\{project}\\{source_stem}.nc" + - time_min: minimum time value (compatible with Timestamp formating) + - time_max: maximum time value (compatible with Timestamp formating) + ex: ".\\{program}\\{project}\\{source_stem}_{time_min.isoformat()}.nc" defaults (dict, optional): Placeholder for any global attributes or variable attributes used in output path. Defaults to None. file_preffix (str, optional): Preffix to add to file name. Defaults to "". @@ -54,35 +88,11 @@ def generate_output_path( path = str(path) # Review file_output path given by config - path_generation_inputs = { - **(defaults or {}), - **{f"{key}": value for key, value in ds.attrs.items() if value}, - **{ - f"variable_{var}_{key}": value - for var in ds.variables - for key, value in ds[var].attrs.items() - }, - **( - { - "source_path": original_source.parent, - "source_stem": original_source.stem, - } - if original_source - else {} - ), - **( - { - "time_min": pd.to_datetime(ds["time"].min().values), - "time_max": pd.to_datetime(ds["time"].max().values), - } - if "time" in ds - else {} - ), - } + path_generation_inputs = get_path_generation_input(ds, original_source) # Generate path and file name - output_path = Path(path.format(**path_generation_inputs)) - file_name = file_name.format(**path_generation_inputs) + output_path = Path(path.format(**(defaults or {}), **path_generation_inputs)) + file_name = file_name.format(**(defaults or {}), **path_generation_inputs) # Retrieve output_format if given in source diff --git a/ocean_data_parser/cli.py b/ocean_data_parser/cli.py index c1e2e272..2cad028e 100644 --- a/ocean_data_parser/cli.py +++ b/ocean_data_parser/cli.py @@ -8,7 +8,7 @@ from loguru import logger from ocean_data_parser import __version__ -from ocean_data_parser.batch.convert import convert as convert +from ocean_data_parser.batch import convert from ocean_data_parser.inspect import inspect_variables as inspect_variables LOG_LEVELS = ["TRACE", "DEBUG", "INFO", "WARNING", "ERROR"] @@ -75,6 +75,7 @@ def emit(self, record): help="Logger level used", default="INFO", envvar="ODPY_LOG_LEVEL", + show_default=True, ) @click.option( "--log-file", type=click.Path(), help="Log to a file.", envvar="ODPY_LOG_FILE" @@ -85,6 +86,44 @@ def emit(self, record): help="Log file level used", default="INFO", envvar="ODPY_LOG_FILE_LEVEL", + show_default=True, +) +@click.option( + "--log-file-rotation", + type=str, + help="Rotate log file at a given interval. Given value must be compatible with pandas.TimeDelta", + default=None, +) +@click.option( + "--log-file-retention", + type=str, + help=( + "Delete log file after a given time period. " + "Given value must be compatible with pandas.TimeDelta (e.g. '1D', '1W'). " + "If None, file will be kept indefinitely." + ), + default=None, +) +@click.option( + "--diagnose/--no-diagnose", + is_flag=True, + default=True, + help="Run loguru diagnose on errors, to see all variable inputs and stacktrace", + show_default=True, +) +@click.option( + "--backtrace/--no-backtrace", + is_flag=True, + default=True, + help="Show stacktrace on error", + show_default=True, +) +@click.option( + "--backtrace-limit", + type=int, + default=5, + help="Limit stacktrace to N lines", + show_default=True, ) @click.option( "--show-arguments", @@ -93,16 +132,38 @@ def emit(self, record): help="Print present argument values", hidden=True, ) -def main(verbose, log_level, log_file, log_file_level, show_arguments): +def main( + verbose, + log_level, + log_file, + log_file_level, + log_file_rotation, + log_file_retention, + backtrace, + backtrace_limit, + diagnose, + show_arguments, +): """Ocean Data Parser command line main interface.""" log_format = VERBOSE_LOG_FORMAT if verbose else LOG_FORMAT logger.add( sys.stderr, level=log_level, + backtrace=backtrace, + diagnose=diagnose, format=VERBOSE_LOG_FORMAT if verbose else LOG_FORMAT, ) + sys.backtrace_limit = backtrace_limit if log_file: - logger.add(log_file, level=log_file_level, format=log_format) + logger.add( + log_file, + level=log_file_level, + format=log_format, + rotation=log_file_rotation, + retention=log_file_retention, + backtrace=backtrace, + diagnose=diagnose, + ) logger.info("ocean-data-parser[{}]: log-level={}", __version__, log_level) if show_arguments: @@ -113,5 +174,8 @@ def main(verbose, log_level, log_file, log_file_level, show_arguments): click.echo(f"log_file_level={log_file_level}") -main.add_command(convert) +main.add_command(convert.cli) main.add_command(inspect_variables) + +if __name__ == "__main__": + main(auto_envar_prefix="ODPY") diff --git a/ocean_data_parser/geo.py b/ocean_data_parser/geo.py index 8331655b..cec6952b 100644 --- a/ocean_data_parser/geo.py +++ b/ocean_data_parser/geo.py @@ -3,7 +3,6 @@ from typing import Union import pandas as pd -from geographiclib.geodesic import Geodesic def read_geojson( @@ -25,7 +24,8 @@ def read_geojson( from shapely.geometry import shape except ImportError: raise RuntimeError( - "Shapely is necessary to read geojson. Install shapely with `pip install shapely`" + "Shapely is necessary to read geojson. " + "Install shapely with `pip install shapely`" ) if not os.path.exists(path): @@ -52,13 +52,15 @@ def get_geo_code(position: list, geographical_areas_collections: list) -> str: position (float,float): [description] collections (list): [description] Returns: - geographical_areas list (str): comma separated list of matching geographical areas + geographical_areas list (str): comma separated list of + matching geographical areas """ try: from shapely.geometry import Point, Polygon except ImportError: raise RuntimeError( - "Shapely is necessary to retrieve geograpical areas. Install shapely with `pip install shapely`" + "Shapely is necessary to retrieve geograpical areas. " + "Install shapely with `pip install shapely`" ) matched_features = [ name.replace(" ", "-") @@ -74,23 +76,33 @@ def get_nearest_station( longitude: float, stations: Union[list[tuple[str, float, float]], pd.DataFrame], max_distance_from_station_km: float = None, - geod: Geodesic = None, + geod: str = "WGS84", ) -> str: """Get the nearest station from a list of reference stations Args: - latitude (float): [description] - longigude (float): [description] - stations Union[list, pd.DataFrame]: List of reference stations [(station, latitude, longitude)] or pandas DataFrame - if a dataframe is passed, the expected colums should be respectively called (station, latitude,longitude) - max_distance_from_station_km (float, optional): Max distance [km] from station to be matched. - geod (Geodesic, optional): [description]. Defaults to None. + latitude (float): target latitude + longigude (float): target longitude + stations Union[list, pd.DataFrame]: List of reference stations + [(station, latitude, longitude)] or pandas DataFrame + if a dataframe is passed, the expected colums should be + respectively called (station, latitude,longitude) + max_distance_from_station_km (float, optional): Max distance in + kilometer from station to be matched. + geod (Geodesic, optional): geographicLib Geodesic model. Defaults to WGS84. Returns: nearest_station (str): Nearest station to the given latitude and longitude """ - if geod is None: - geod = Geodesic.WGS84 # define the WGS84 ellipsoid + try: + from geographiclib.geodesic import Geodesic + + geod = getattr(Geodesic, geod) # define the WGS84 ellipsoid + except ImportError: + raise RuntimeError( + "geographiclib is necessary to run get_nearest_station. " + "Install geographiclib with `pip install geographicLib`" + ) if isinstance(stations, pd.DataFrame): stations = stations[["station", "latitude", "longitude"]].values diff --git a/ocean_data_parser/inspect.py b/ocean_data_parser/inspect.py index 9a293947..9060ecf8 100644 --- a/ocean_data_parser/inspect.py +++ b/ocean_data_parser/inspect.py @@ -161,7 +161,11 @@ def variables( default=1, flag_value=os.cpu_count(), is_flag=False, - help=f"Load files in parallele with n processors. If the option is set as a flag, all the processors available (={os.cpu_count()}) will be used.", + help=( + "Load files in parallele with n processors. " + "If the option is set as a flag, " + f"all the processors available (={os.cpu_count()}) will be used." + ), show_default=True, ) @click.option( diff --git a/ocean_data_parser/metadata/__init__.py b/ocean_data_parser/metadata/__init__.py index 5bc9c825..e69de29b 100644 --- a/ocean_data_parser/metadata/__init__.py +++ b/ocean_data_parser/metadata/__init__.py @@ -1 +0,0 @@ -from . import pdc diff --git a/ocean_data_parser/metadata/nerc.py b/ocean_data_parser/metadata/nerc.py index b175390d..49318377 100644 --- a/ocean_data_parser/metadata/nerc.py +++ b/ocean_data_parser/metadata/nerc.py @@ -18,5 +18,21 @@ def get_vocabulary(vocab: str) -> pd.DataFrame: def get_vocabulary_term(vocab: str, id: str) -> dict: url = f"http://vocab.nerc.ac.uk/collection/{vocab}/current/{id}/?_profile=nvs&_mediatype=application/ld+json" with requests.get(url) as response: - json_text = response.text - return json.loads(json_text) + return response.json() + + +def get_platform_vocabulary(id: str) -> dict: + result = get_vocabulary_term("C17", id) + # Parse the json data in the definition field + attrs = json.loads(result["definition"]["@value"])["node"] + return { + "platform_name": result["prefLabel"]["@value"], + "platform_type": attrs.get("platformclass"), + "country_of_origin": attrs.get("country"), + "platform_owner": attrs.get("title"), + "platform_id": id, + "ices_platform_code": id, + "wmo_platform_code": attrs.get("IMO"), + "call_sign": attrs.get("callsign"), + "sdn_platform_urn": result["identifier"], + } diff --git a/ocean_data_parser/parsers/amundsen.py b/ocean_data_parser/parsers/amundsen.py index d246676f..8c588b76 100644 --- a/ocean_data_parser/parsers/amundsen.py +++ b/ocean_data_parser/parsers/amundsen.py @@ -1,11 +1,10 @@ """ -# Amundsen - - - -Historically ArcticNet and the Amundsen Siences. +This module contains the parser for the Amundsen INT format. +This format is used to store oceanographic data generated by +the [Amundsen Science](https://amundsenscience.com/) and +[ArcticNet](https://arcticnet.ulaval.ca/) programs. """ -import json + import logging import re @@ -13,7 +12,7 @@ import xarray as xr from gsw import z_from_p -from ocean_data_parser._version import __version__ +from ocean_data_parser import __version__ from ocean_data_parser.parsers.utils import get_history_handler, standardize_dataset from ocean_data_parser.vocabularies.load import amundsen_vocabulary @@ -52,8 +51,14 @@ def _standardize_attribute_value(value: str, name: str = None): """ if name in string_attributes or not isinstance(value, str): return value - elif re.match(r"\d\d-\w\w\w-\d\d\d\d \d\d\:\d\d\:\d\d", value): - return pd.to_datetime(value, utc=(name and "utc" in name)) + elif re.fullmatch(r"\d\d-\w\w\w-\d\d\d\d \d\d\:\d\d\:\d\d", value): + return pd.to_datetime( + value, utc=(name and "utc" in name), format="%d-%b-%Y %H:%M:%S" + ) + elif re.fullmatch(r"\d\d-\w\w\w-\d\d\d\d \d\d\:\d\d\:\d\d.\d", value): + return pd.to_datetime( + value, utc=(name and "utc" in name), format="%d-%b-%Y %H:%M:%S.%f" + ) elif re.match(r"^-{0,1}\d+\.\d+$", value): return float(value) elif re.match(r"^-{0,1}\d+$", value): @@ -70,8 +75,6 @@ def int_format( ) -> xr.Dataset: """Parse Amundsen INT format. - The Amundsen INT format is a tabular format - Args: path (str): file path to parse. encoding (str, optional): File encoding. Defaults to "Windows-1252". @@ -79,7 +82,7 @@ def int_format( generate_depth (bool, optional): Generate depth variable. Defaults to True. Returns: - xr.Dataset: xarray compliant with CF-1.6 + xr.Dataset """ nc_logger, nc_handler = get_history_handler() logger.addHandler(nc_handler) @@ -108,6 +111,18 @@ def int_format( elif ":" in line: key, value = line.strip()[1:].split(":", 1) metadata[key.strip()] = value.strip() + elif line == "% Fluorescence [ug/L]": + metadata["Fluo"] = "Fluorescence [ug/L]" + elif line == "% Conservative Temperature (TEOS-10) [deg C]": + metadata["CONT"] = "Conservative Temperature (TEOS-10) [deg C]" + elif line == "% In situ density TEOS10 ((s, t, p) - 1000) [kg/m^3]": + metadata["D_CT"] = "In situ density TEOS10 ((s, t, p) - 1000) [kg/m^3]" + elif line == "% Potential density TEOS10 ((s, t, 0) - 1000) [kg/m^3]": + metadata[ + "D0CT" + ] = "Potential density TEOS10 ((s, t, 0) - 1000) [kg/m^3]" + elif line == "% Potential density TEOS10 (s, t, 0) [kg/m^3]": + metadata["D0CT"] = "Potential density TEOS10 (s, t, 0) [kg/m^3]" elif re.match(r"% .* \[.+\]", line): logger.warning( "Unknown variable name will be saved to unknown_variables_information: %s", @@ -191,7 +206,7 @@ def int_format( ) ds["instrument_depth"] = -z_from_p(ds[pressure[0]], latitude) - # Map varibles to vocabulary + # Map variables to vocabulary variables_to_rename = {} for var in ds: if var not in variables: @@ -215,7 +230,7 @@ def int_format( if ( var_units is None # Consider first if no units or var_units == item.get("units") - or (accepted_units and re.match(accepted_units, var_units)) + or (accepted_units and re.fullmatch(accepted_units, var_units)) ): if "rename" in item: variables_to_rename[var] = item["rename"] diff --git a/ocean_data_parser/parsers/dfo/__init__.py b/ocean_data_parser/parsers/dfo/__init__.py index eddea299..6ea29754 100644 --- a/ocean_data_parser/parsers/dfo/__init__.py +++ b/ocean_data_parser/parsers/dfo/__init__.py @@ -1 +1,2 @@ +__all__ = ["ios", "nafc", "odf"] from . import ios, nafc, odf diff --git a/ocean_data_parser/parsers/dfo/ios_source/IosObsFile.py b/ocean_data_parser/parsers/dfo/ios_source/IosObsFile.py index 41be76c9..2f63b113 100644 --- a/ocean_data_parser/parsers/dfo/ios_source/IosObsFile.py +++ b/ocean_data_parser/parsers/dfo/ios_source/IosObsFile.py @@ -1,15 +1,16 @@ """ - Python class to read IOS data files and store data for conversion to netcdf format - Changelog Version - 0.1: July 15 2019: - Convert python scripts and functions into a python class - 0.2: August 2023: - Migrate the code to the ocean-data-parser package and reduce code base - Authors: - Pramod Thupaki (pramod.thupaki@hakai.org) - Jessy Barrette +Python class to read IOS data files and store data for conversion to netcdf format +Changelog Version + 0.1: July 15 2019: + Convert python scripts and functions into a python class + 0.2: August 2023: + Migrate the code to the ocean-data-parser package and reduce code base +Authors: + Pramod Thupaki (pramod.thupaki@hakai.org) + Jessy Barrette """ + import json import logging import re @@ -60,7 +61,10 @@ "institution": "DFO IOS", "ices_edmo_code": 4155, "sdn_institution_urn": "SDN:EDMO::4155", - "infoUrl": "https://science.gc.ca/site/science/en/educational-resources/marine-and-freshwater-sciences/institute-ocean-sciences", + "infoUrl": ( + "https://science.gc.ca/site/science/en/educational-resources" + "/marine-and-freshwater-sciences/institute-ocean-sciences" + ), "country": "Canada", "ioc_country_code": 18, "naming_authority": "ca.gc.ios", @@ -97,7 +101,9 @@ def get_dtype_from_ios_name(ios_name): return float -ODF_SHELL_HEADER_SECTIONS = ( +IOS_SHELL_HEADER_SECTIONS = { + "FILE", + "LOCATION", "COMMENTS", "REMARK", "ADMINISTRATION", @@ -105,7 +111,8 @@ def get_dtype_from_ios_name(ios_name): "HISTORY", "DEPLOYMENT", "RECOVERY", -) + "CALIBRATION", +} class IosFile(object): @@ -138,6 +145,7 @@ def __init__(self, filename): self.data = None self.deployment = None self.recovery = None + self.calibration = None self.obs_time = None self.vocabulary_attributes = None self.history = None @@ -157,7 +165,7 @@ def __init__(self, filename): self.status = 1 def import_data(self): - sections_available = self.get_list_of_sections() + sections_available = set(self.get_list_of_sections()) self.start_dateobj, self.start_date = self.get_date(opt="start") self.end_dateobj, self.end_date = ( self.get_date(opt="end") if "END TIME" in self.file else (None, None) @@ -175,12 +183,10 @@ def import_data(self): self.deployment = self.get_section("DEPLOYMENT") if "RECOVERY" in sections_available: self.recovery = self.get_section("RECOVERY") + if "CALIBRATION" in sections_available: + self.calibration = self.get_section("CALIBRATION") - unparsed_sections = [ - section - for section in sections_available - if section not in ODF_SHELL_HEADER_SECTIONS - ] + unparsed_sections = sections_available - IOS_SHELL_HEADER_SECTIONS if unparsed_sections: logger.warning( "Unknown sections: %s", @@ -881,7 +887,7 @@ def rename_date_time_variables(self): history += [f"rename variable '{chan}' -> 'Time'"] rename_channels[id] = "Time" else: - logger.warning(f"Unkown date time channel %s", chan) + logger.warning("Unkown date time channel %s", chan) self.channels["Name"] = rename_channels @@ -930,6 +936,7 @@ def _format_attribute_value(value): **_format_attributes("location"), **_format_attributes("deployment", "deployment_"), **_format_attributes("recovery", "recovery_"), + "calibration": json.dumps(self.calibration) if self.calibration else None, "comments": str(self.comments) if self.comments else None, # TODO missing file_remarks diff --git a/ocean_data_parser/parsers/dfo/nafc.py b/ocean_data_parser/parsers/dfo/nafc.py index 499bbde4..b6615612 100644 --- a/ocean_data_parser/parsers/dfo/nafc.py +++ b/ocean_data_parser/parsers/dfo/nafc.py @@ -1,13 +1,11 @@ """ The Fisheries and Oceans Canada - Newfoundland and Labrador Region - -North Atlantic Fisheries Centre - - - +North Atlantic Fisheries Centre (NAFC) is a research facility located in St. John's, Newfoundland and Labrador. """ -import logging +import inspect import re +from functools import lru_cache from pathlib import Path from typing import Union @@ -15,95 +13,168 @@ import numpy as np import pandas as pd import xarray as xr +from loguru import logger +from ocean_data_parser.parsers import seabird from ocean_data_parser.parsers.utils import standardize_dataset from ocean_data_parser.vocabularies.load import ( dfo_nafc_p_file_vocabulary, dfo_platforms, ) -logger = logging.getLogger(__name__) MODULE_PATH = Path(__file__).parent p_file_vocabulary = dfo_nafc_p_file_vocabulary() -p_file_shipcode = dfo_platforms() +p_file_shipcode = dfo_platforms().drop(columns=["accepted_platform_name"]) global_attributes = { "Conventions": "CF-1.6,CF-1.7,CF-1.8,ACDD-1.3,IOOS 1.2", "naming_authority": "ca.gc.nafc", } +METQA_ATTRS_MAPPING = { + "wind_speed": "wind_speed_knots", + "bar_pressure": "pressure_bars", + "air_temp_dry": "air_dry_temp_celsius", + "air_temp_wet": "air_wet_temp_celsius", + "bergs": "ice_bergs", + "sit_and_trend": "ice_sit_and_trend", +} + +METQA_DTYPES = { + "wind_speed_knots": pd.Int64Dtype(), + "wind_dir": pd.Int64Dtype(), + "pressure_bars": float, + "air_dry_temp_celsius": float, + "air_wet_temp_celsius": float, + "swell_dir": pd.Int64Dtype(), + "swell_period": pd.Int64Dtype(), + "swell_height": float, + "ice_conc": pd.Int64Dtype(), + "ice_stage": pd.Int64Dtype(), + "ice_bergs": pd.Int64Dtype(), + "ice_sit_and_trend": pd.Int64Dtype(), +} + + +def _traceback_error_line(): + current_frame = inspect.currentframe() + previous_frame = current_frame.f_back.f_back + line_number = previous_frame.f_lineno + cmd_line = Path(__file__).read_text().split("\n")[line_number - 1].strip() + return f"<{previous_frame.f_code.co_name} line {line_number}>: {cmd_line}" + -def _int(value: str, null_values=None) -> int: +def _int(value: str, null_values=None, level="WARNING", match: str = None) -> int: """Attemp to convert string to int, return None if empty or failed""" - if not value.strip(): + if not value or not value.strip() or value in (null_values or []): return + if match: + new_value = re.match(match, value) + if new_value: + value = [item for item in new_value.groups() if item][0] + else: + logger.log( + level, + "Failed to convert: {} => int('{}')", + _traceback_error_line(), + value, + ) + return pd.NA + if null_values and value in null_values: + return pd.NA + elif value in ("0.", ".0"): + return 0 try: value = int(value) if null_values and value in null_values: - return None - return value - except TypeError: - logger.error("Failed to convert string=%s to int", value) + return pd.NA + return int(value) + except ValueError: + logger.log( + level, + "Failed to convert: {} => int('{}')", + _traceback_error_line(), + value, + ) + return pd.NA -def _float(value: str, null_values=None) -> float: +def _float(value: str, null_values=None, level="WARNING") -> float: """Attemp to convert string to float, return None if empty or failed""" - if not value.strip(): + if not value or not value.strip() or value in (null_values or []): return try: value = float(value) - if null_values and value in null_values: - return None - return value - except TypeError: - logger.error("Failed to convert string=%s to float", value) + return None if null_values and value in null_values else value + except ValueError: + logger.log( + level, + "Failed to convert: {} => float('{}')", + _traceback_error_line(), + value, + ) + return pd.NA def _get_dtype(var: str): return int if var == "scan" else float -def soft_catch_errors(function): - def wrap(*args, **kwargs): - try: - value = function(*args, **kwargs) - return value - except ValueError: - logger.error("Failed to parse %s", function.__name__, exc_info=True) - return {} - - return wrap +def _parse_ll(deg: float, min: float) -> float: + """Combine deg and min values from latitude and longitude to decimal degrees""" + if pd.isna(deg) or pd.isna(min): + return + dir = -1 if deg < 0 else 1 + return deg + (dir * min / 60) -@soft_catch_errors def _parse_pfile_header_line1(line: str) -> dict: """Parse first row of the p file format which contains location and instrument information.""" + + if line[44:46] == "60": + # Fix some dates are using 60 minutes which is not compatible with pandas datetime + dt = pd.Timedelta("1min") + line = line[:44] + "00" + line[46:] + else: + dt = pd.Timedelta("0") + return dict( - ship_code=line[:2], - trip=_int(line[2:5]), - station=_int(line[5:8]), - latitude=_float(line[9:12]) + float(line[13:18]) / 60, - longitude=_float(line[19:23]) + float(line[24:29]) / 60, - time=pd.to_datetime(line[30:46], format="%Y-%m-%d %H:%M", utc=True), - sounder_depth=_int(line[47:51]) - if line[47:51] not in ("9999", "0000") - else None, # water depth in meters 9999 or 0000 = not known - instrument=line[ - 52:57 - ], # Sxxxxx is a seabird ctd XBTxx is an XBT for an XBT, A&C= Sippican probe, A&B mk9, B&D= Spartan probe, C&D mk12" - set_number=_int(line[58:61]), # usually same as stn + # ship_code=line[:2], + # trip=_int(line[2:5], level="ERROR"), + # station=_int(line[5:8], level="ERROR"), + latitude=_parse_ll( + _float(line[9:12], level="ERROR"), _float(line[13:18], level="ERROR") + ), + longitude=_parse_ll( + _float(line[19:23], level="ERROR"), _float(line[24:29], level="ERROR") + ), + time=pd.to_datetime(line[30:46], format="%Y-%m-%d %H:%M", utc=True) + dt, + sounder_depth=_int( + line[47:51], ["9999", "0000", "-999"] + ), # water depth in meters + instrument=line[52:57], # see note below + set_number=_int( + line[58:61], + ("SET", "xxx", "set", "XXX", "ctd", "xbt", "Stn", "STN", "stn", "nil"), + match=r"\s*(\d+)\.?|[sS](\d+)|[sS][tT](\d+)|\#(\d+)", + ), # usually same as stn cast_type=line[62], # V vertical profile T for tow comment=line[62:78], card_1_id=line[79], ) + # instrument = # + # Sxxxxx is a seabird ctd + # XBTxx is an XBT for an XBT, + # A&C= Sippican probe, + # A&B mk9, B&D= Spartan probe, + # C&D mk12 -@soft_catch_errors def _parse_pfile_header_line2(line: str) -> dict: return dict( - ship_code=line[:2], - trip=_int(line[2:5]), - station=_int(line[5:8]), + # ship_code=line[:2], + # trip=_int(line[2:5]), + # station=_int(line[5:8]), scan_count=_int(line[9:15]), # number of scan lines in file sampling_rate=_float(line[16:21]), # 00.00 for unknown file_type=line[22], # A for ASCII B for binary data @@ -129,35 +200,60 @@ def _parse_pfile_header_line2(line: str) -> dict: ) -@soft_catch_errors def _parse_pfile_header_line3(line: str) -> dict: """Parse P file 3 metadata line which present environment metadata""" + if not line or not line.strip() or len(line) < 79: + logger.warning("Missing pfile header line 3") + return {} return dict( - ship_code=line[:2], - trip=_int(line[2:5]), - station=_int(line[5:8]), - cloud=_int(line[9]), # i1, + # ship_code=line[:2], + # trip=_int(line[2:5]), + # station=_int(line[5:8]), + cloud=_int(line[9], null_values=["x", "-", "/", "."]), # i1, wind_dir=_int(line[11:13]) * 10 - if line[11:13].strip() and line[11:13] != "99" + if line[11:13].strip() and line[11:13] not in ["99", "98"] else None, # in 10 degree steps (eg 270 is=27) - wind_speed_knots=_int(line[14:16]), # i2,knots s= cale + wind_speed_knots=_int( + line[14:16], + null_values=[ + "99", + ], + ), # i2,knots s= cale ww_code=_int(line[17:19]), # i2, pressure_bars=_float(line[20:26], [-999.0, -999.9]), # pressure mil-= bars air_dry_temp_celsius=_float( - line[27:32], [-99.0, -99.9, 999.9] + line[27:32], [-99.0, -99.9, 999.9, -49.9] ), # f5.1,tem= p °C air_wet_temp_celsius=_float( - line[33:38], [-99.0, 99.9, -99.9, 999.9] + line[33:38], [-99.0, 99.9, -99.9, 999.9, -49.9] ), # f5.1,tem= p °C - waves_period=_int(line[39:41]), # i2, - waves_height=_int(line[42:44]), # i2, - swell_dir=_int(line[45:47]) * 10 if line[45:47].strip() else None, # i2, - swell_period=_int(line[48:50]), # i2, - swell_height=_float(line[51:53]), # i2, + waves_period=_int( + line[39:41], null_values=["XX", "- ", "99"], match=r"\s*(\d+)\.?" + ), # i2, + waves_height=_float( + line[42:44], + null_values=[ + "99", + ], + ), # i2, + swell_dir=_int(line[45:47]) * 10 + if line[45:47].strip() + and line[45:47] + not in [ + "99", + ] + else None, # i2, + swell_period=_int(line[48:50], null_values=["XX", "- ", "99"]), # i2, + swell_height=_float( + line[51:53], + null_values=[ + "99", + ], + ), # i2, ice_conc=_int(line[54]), # i1, ice_stage=_int(line[56]), # i1, ice_bergs=_int(line[58]), # i1, - ice_SandT=_int(line[60]), # i1, + ice_sit_and_trend=_int(line[60]), # i1, card_8_id=_int(line[79]), # i1 ,=8 ) @@ -188,15 +284,30 @@ def _get_range(attrs: dict) -> tuple: return {item["name"]: {"actual_range": _get_range(item)} for item in spans} -def _get_ship_code_metadata(shipcode: Union[int, str]) -> dict: - shipcode = f"{shipcode:02g}" if isinstance(shipcode, int) else shipcode - if p_file_shipcode["dfo_newfoundland_ship_code"].str.match(shipcode).any(): +def _get_platform_by_nafc_platform_code(platform_code: Union[int, str]) -> dict: + platform_code = ( + f"{platform_code:02g}" + if isinstance(platform_code, int) + else platform_code.upper() + ) + if p_file_shipcode["dfo_nafc_platform_code"].str.match(platform_code).any(): return ( - p_file_shipcode.query(f"dfo_newfoundland_ship_code == '{shipcode}'") + p_file_shipcode.query(f"dfo_nafc_platform_code == '{platform_code}'") .iloc[0] .to_dict() ) - logger.warning("Unknown p-file shipcode=%s", shipcode) + logger.warning("Unknown dfo_nafc_platform_code={}", platform_code) + return {} + + +def _get_platform_by_nafc_platform_name(platform_name: str) -> dict: + if platform_name in p_file_shipcode["dfo_nafc_platform_name"].tolist(): + return ( + p_file_shipcode.query(f"dfo_nafc_platform_name == '{platform_name}'") + .iloc[0] + .to_dict() + ) + logger.warning("Unknown dfo_nafc_platform_name={}", platform_name) return {} @@ -211,6 +322,8 @@ def _pfile_history_to_cf(lines: list) -> str: """ # """Convert history to cf format: 2022-02-02T00:00:00Z - ...""" + if not lines: + return "" history_timestamp = re.search( r"-- HISTORY --> (\w+ \w\w\w\s+\d+ \d{2}:\d{2}:\d{2} \d{4})", lines[0] @@ -228,23 +341,37 @@ def _pfile_history_to_cf(lines: list) -> str: return "".join([f"{timestamp} - {line}" for line in lines[1:]]) +def _get_pfile_variable_vocabulary(variable: str, instrument: str = None) -> dict: + """Retrieve variable vocabulary""" + if variable == "xxx": + return [] + matched_legacy_p_code = p_file_vocabulary.apply( + lambda x: re.fullmatch(x["legacy_p_code"], variable, re.IGNORECASE), axis=1 + ).notna() + + if not any(matched_legacy_p_code): + logger.warning("No vocabulary is available for variable={}", variable) + return [] + + matching_vocabulary = p_file_vocabulary.loc[matched_legacy_p_code].query( + f"(accepted_instruments.isna() or " + f"accepted_instruments in '{instrument or ''}' )" + ) + if matching_vocabulary.empty: + logger.warning("No vocabulary is available for variable={}", variable) + return [] + return matching_vocabulary.to_dict(orient="records") + + def pfile( file: str, encoding: str = "UTF-8", rename_variables: bool = True, generate_extra_variables: bool = True, + encoding_errors: str = "strict", ) -> xr.Dataset: """Parse DFO NAFC oceanography p-file format - The NAFC oceanography p-files format is according - to the pfile documentation,: - - 1. NAFC_Y2K_HEADER - 2. 3 single line 80 byte headers, the formats of which is described on an attached page. - 3. A variable length block of processing history information - 4. A line of channel name identifiers - 5. A start of data flag line -- DATA -- - Args: file (str): file path encoding (str, optional): file encoding. Defaults to "UTF-8". @@ -260,30 +387,30 @@ def pfile( xr.Dataset: Parser dataset """ - def _check_ship_trip_stn(): + def _parse_ship_trip_stn(): """Review if the ship,trip,stn string is the same - accorss the 3 metadata rows""" - ship_trip_stn = [line[:9] for line in metadata_lines[1:]] - assert ( - len(set(ship_trip_stn)) == 1 - ), f"Ship,trip,station isn't consistent: {set(ship_trip_stn)}" - - def _get_variable_vocabulary(variable: str) -> dict: - """Retrieve variable vocabulary""" - matching_vocabulary = p_file_vocabulary.query( - f"legacy_p_code == '{variable}' and " - f"(accepted_instruments.isna() or " - f"accepted_instruments in '{ds.attrs.get('instrument','')}' )" + accorss the 3 metadata rows and the file name.""" + ship_trip_stn = [line[:8] for line in metadata_lines[1:] if line.strip()] + [ + file.stem + ] + if len(set(ship_trip_stn)) != 1: + logger.warning( + "Ship,trip,station isn't consistent: {}. " + "The file name will be considered = {}.", + set(ship_trip_stn), + file.stem, + ) + return dict( + ship_code=file.stem[:2], + trip=int(file.stem[2:5]), + station=int(file.stem[5:8]), ) - if matching_vocabulary.empty: - logger.warning("No vocabulary is available for variable=%s", variable) - return [] - return matching_vocabulary.to_dict(orient="records") + file = Path(file) line = None header = {} section = None - with open(file, encoding=encoding) as file_handle: + with open(file, encoding=encoding, errors=encoding_errors) as file_handle: # Read the four first lines to extract the information original_header = [file_handle.readline() for _ in range(4)] metadata_lines = original_header[:4] @@ -307,6 +434,19 @@ def _get_variable_vocabulary(variable: str) -> dict: # Define each fields width based on the column names names = re.findall(r"\w+", previous_line) + if len(names) != len(set(names)): + # Rename duplicated names + logger.warning("Column names aren't unique: {}", names) + duplicated_names = [] + for index, name in enumerate(names): + if names.count(name) > 1: + new_name = f"{name}{names[:index].count(name)}" + logger.warning("Rename {} to {}", name, new_name) + duplicated_names += [new_name] + else: + duplicated_names += [name] + + names = duplicated_names # Read data section # TODO confirm that 5+12 character width is constant @@ -316,12 +456,16 @@ def _get_variable_vocabulary(variable: str) -> dict: engine="python", names=names, dtype={name: _get_dtype(name) for name in names}, + encoding_errors=encoding_errors, ).to_xarray() + if len(ds.index) == 0: + logger.error("No data found in file") + # Review datatypes - if any([dtype == object for var, dtype in ds.dtypes.items()]): + if any([dtype == object for _, dtype in ds.dtypes.items()]): logger.warning( - "Some columns dtype=object which suggest that the file data wasn't correctely parsed." + "Some columns dtype=object suggest the file data wasn't correctely parsed." ) # Review metadata @@ -329,22 +473,22 @@ def _get_variable_vocabulary(variable: str) -> dict: raise TypeError( "File header doesn't contain pfile first line 'NAFC_Y2K_HEADER'" ) - _check_ship_trip_stn() # Convert dataframe to an xarray and populate information ds.attrs.update( { "id": metadata_lines[1][:8], **global_attributes, - **_parse_pfile_header_line1(metadata_lines[1]), - **_parse_pfile_header_line2(metadata_lines[2]), - **_parse_pfile_header_line3(metadata_lines[3]), + **_parse_ship_trip_stn(), + **(_parse_pfile_header_line1(metadata_lines[1]) or {}), + **(_parse_pfile_header_line2(metadata_lines[2]) or {}), + **(_parse_pfile_header_line3(metadata_lines[3]) or {}), "history": header.get("HISTORY"), } ) ds.attrs["original_header"] = "\n".join(original_header) ds.attrs["history"] = _pfile_history_to_cf(header.get("HISTORY")) - ds.attrs.update(_get_ship_code_metadata(ds.attrs.get("ship_code", {}))) + ds.attrs.update(_get_platform_by_nafc_platform_code(ds.attrs.get("ship_code", {}))) # Move coordinates to variables: coords = ["time", "latitude", "longitude"] @@ -358,10 +502,12 @@ def _get_variable_vocabulary(variable: str) -> dict: extra_vocabulary_variables = [] for var in ds.variables: ds[var].attrs.update(variables_span.get(var, {})) - variable_attributes = _get_variable_vocabulary(var) + variable_attributes = _get_pfile_variable_vocabulary( + var, ds.attrs.get("instrument") + ) if not variable_attributes: - logger.warning("Missing vocabulary for p-file variable=%s", var) continue + ds[var].attrs.update(variable_attributes[0]) for extra in variable_attributes[1:]: extra_vocabulary_variables += [ @@ -388,14 +534,15 @@ def _get_variable_vocabulary(variable: str) -> dict: if generate_extra_variables: for name, var, attrs in extra_vocabulary_variables: if name in ds: - logger.warning( + logger.info( ( "Extra variable is already in dataset and will be ignored. " - "name=%s, attrs=%s is already in dataset and will be ignored" + "name={}, attrs={} is already in dataset and will be ignored" ), - var, + var.name, attrs, ) + continue apply_func = attrs.pop("apply_func", None) new_data = ( eval( @@ -414,10 +561,208 @@ def _get_variable_vocabulary(variable: str) -> dict: if apply_func not in (None, np.nan) else var ) - # TODO add to this history new variables generated + ds.attrs[ + "history" + ] += f"\n{pd.Timestamp.now()} - Generated variable {name} = {apply_func}" + attrs["source"] = f"Generated variable {name} = {apply_func}" ds[name] = (var.dims, new_data.data, {**var.attrs, **attrs}) # standardize ds = standardize_dataset(ds) return ds + + +@logger.catch +def _parse_lat_lon(latlon: str) -> float: + """Parse latitude and longitude string to float""" + if not latlon: + logger.error("No latitude or longitude provided") + return + deg, min, dir = latlon.split(" ") + return (-1 if dir in ("S", "W") else 1) * (int(deg) + float(min) / 60) + + +@logger.catch +@lru_cache +def _get_metqa_table(file) -> pd.DataFrame: + """Load NAFC metqa table which contains each files assoicated weather data""" + df = pd.read_csv(file, sep="\s*\,", engine="python") + df.columns = [ + col.lower().split("[")[0].strip().replace(" ", "_") for col in df.columns + ] + df["time"] = pd.to_datetime(df["date"] + " " + df["time"], utc=True) + df["latitude"] = df["latitude"].apply(lambda x: _parse_lat_lon(x)) + df["longitude"] = df["longitude"].apply(lambda x: _parse_lat_lon(x)) + df = df.drop(columns=["date"]) + df = df.rename(columns=METQA_ATTRS_MAPPING).astype(METQA_DTYPES) + return df + + +def _add_metqa_info_to_pcvn(file: Path, match_metqa_file) -> Path: + """Find the matching metqa table to the pcnv file""" + + glob_expression = f"{file.stem.rsplit('_',1)[0]}_metqa_*.csv" + metqa_file = list(file.parent.glob(glob_expression)) + if metqa_file and len(metqa_file) == 1: + logger.debug("Load weather data from metqa file={}", metqa_file[0]) + df = _get_metqa_table(metqa_file[0]) + metadata = df.query(f"station == '{file.stem}'") + if metadata.empty: + logger.warning("No station={} in metqa file={}", file.stem, metqa_file) + return {} + return metadata.iloc[0].dropna().to_dict() + elif metqa_file and len(metqa_file) > 1: + logger.error( + "Multiple metqa files found={} for path={},glob={}", + metqa_file, + file.parent, + glob_expression, + ) + else: + level = "WARNING" if match_metqa_file else "DEBUG" + logger.log( + level, + "No metqa table file found path={},glob={}", file.parent, glob_expression + ) + return {} + + +def pcnv( + path: Path, + rename_variables: bool = True, + generate_extra_variables: bool = True, + global_attributes: dict = None, + encoding: str = "UTF-8", + encoding_errors: str = "strict", + match_metqa_table: bool = False, +) -> xr.Dataset: + """DFO NAFC pcnv file format parser + + The pcnv format essentially a seabird cnv file format + with NAFC specific inputs within the manual section. + + Args: + path (Path): pcvn file path + rename_variables (bool, optional): Rename variables to + DFO BODC names. Defaults to True. + generate_extra_variables (bool, optional): Generate extra + vocabulary variables. Defaults to True. + global_attributes (dict, optional): Global attributes to add to the dataset. + match_metqa_table (bool, optional): Match metqa table to the file if + available within same directory. Defaults to True. + + Returns: + xr.Dataset + """ + + def _pop_attribute_from(names: list): + """Pop attribute from dataset""" + for name in names: + if name in ds.attrs: + return ds.attrs.pop(name) + + if "comment" not in names[0].lower(): + logger.error("No matching attribute found in {}", names) + + def get_vocabulary(**kwargs): + return p_file_vocabulary.query( + " and ".join(f"{key} == '{value}'" for key, value in kwargs.items()) + ).to_dict(orient="records") + + path = Path(path) + ds = seabird.cnv( + path, + encoding=encoding, + encoding_errors=encoding_errors, + xml_parsing_error_level="WARNING", + ) + + # Map global attributes + ship_trip_seq_station = re.search( + r"(?P\w{3})(?P\d{3})_(?P\d{4})_(?P\d{3})", + ds.attrs.pop("vessel_trip_seq_stn", ""), + ) + if not ship_trip_seq_station: + logger.error( + "Unable to parse ship_trip_seq_station from VESEL/TRIP/SEQ STN= {}", + ds.attrs.get("vessel_trip_seq_stn", ""), + ) + ds.attrs.update( + { + "dfo_nafc_platform_name": ship_trip_seq_station["dfo_nafc_platform_name"], + **_get_platform_by_nafc_platform_name( + ship_trip_seq_station["dfo_nafc_platform_name"] + ), + "trip": _int(ship_trip_seq_station["trip"]), + "year": _int(ship_trip_seq_station["year"]), + "station": _int(ship_trip_seq_station["stn"]), + "time": pd.to_datetime(ds.attrs.pop("date_time"), utc=True), + "latitude": _parse_lat_lon( + _pop_attribute_from( + ["latitude", "latitude_xx_xx.xx", "latitude_xx_xx.xx_n"] + ) + ), + "longitude": _parse_lat_lon( + _pop_attribute_from( + ["longitude", "longitude_xx_xx.xx", "longitude _xx_xx.xx_w"] + ) + ), + "sounder_depth": ds.attrs.pop("sounding_depth_m", None), + "instrument": ds.attrs.pop("probe_type", None), + "set_number": _int(ds.attrs.pop("xbt_number", None)) + or _int(ds.attrs.pop("ctd_number", None)), + "format": ds.attrs.pop("format", None), + "commment": _pop_attribute_from(["comments", "comments_14_char"]), + # "trip_tag": ds.attrs.pop("trip_tag", None), + "vnet": ds.attrs.pop("vnet", None), + "do2": ds.attrs.pop("do2", None), + "bottles": _int(ds.attrs.pop("bottles", None)), + **_add_metqa_info_to_pcvn(path,match_metqa_table), + **(global_attributes or {}), + } + ) + + # Move coordinates to variables + coords = ["time", "latitude", "longitude"] + p_file_vocabulary + for coord in coords: + if coord in ds.attrs: + ds[coord] = ds.attrs[coord] + ds[coord].attrs = get_vocabulary(variable_name=coord)[0] + + ds = ds.set_coords([coord for coord in coords if coord in ds]) + + # Map variable attributes to pfile vocabulary via long_name + variables_new_name = {} + for variable in ds.variables: + if variable in coords: + continue + variable_attributes = get_vocabulary( + long_name=ds[variable].attrs.get("long_name", variable) + ) + if not variable_attributes: + logger.warning("Missing vocabulary for variable={}", variable) + continue + + if rename_variables and variable_attributes[-1].get("variable_name"): + variables_new_name[variable] = variable_attributes[-1].pop("variable_name") + + ds[variable].attrs.update(variable_attributes[-1]) + if not generate_extra_variables: + continue + + for extra in variable_attributes[0:-1]: + new_var = extra.pop("variable_name", variable) + if new_var == "depth" and "depSM" in ds.variables: + continue + logger.debug("Generate extra variable={}", new_var) + ds[new_var] = (ds[variable].dims, ds[variable].data, extra) + + if rename_variables: + logger.debug("Rename variables to NAFC standard: {}", variables_new_name) + ds = ds.rename(variables_new_name) + + ds = standardize_dataset(ds) + + return ds diff --git a/ocean_data_parser/parsers/dfo/odf_source/attributes.py b/ocean_data_parser/parsers/dfo/odf_source/attributes.py index e870bff4..d519f8ee 100644 --- a/ocean_data_parser/parsers/dfo/odf_source/attributes.py +++ b/ocean_data_parser/parsers/dfo/odf_source/attributes.py @@ -20,7 +20,14 @@ logger = logging.getLogger(__name__) stationless_programs = ("Maritime Region Ecosystem Survey",) + +# Transform platform name to a list of accepted platform names reference_platforms = dfo_platforms() +reference_platforms["accepted_platform_name"] = reference_platforms[ + "accepted_platform_name" +].str.split("|") +reference_platforms = reference_platforms.explode("accepted_platform_name") + section_prefix = { "EVENT_HEADER": "event_", "INSTRUMENT_HEADER": "instrument_", @@ -43,11 +50,13 @@ def _generate_platform_attributes(platform: str) -> dict: r"CCGS_*\s*|CGCB\s*|FRV\s*|NGCC\s*|^_|MV\s*", "", platform ).strip() matched_platform = get_close_matches( - platform.lower(), reference_platforms["platform_name"] + platform.lower(), reference_platforms["accepted_platform_name"], n=1 ) if matched_platform: return ( - reference_platforms.query(f"platform_name == '{matched_platform[0]}'") + reference_platforms.query( + f"accepted_platform_name == '{matched_platform[0]}'" + ) .iloc[0] .to_dict() ) diff --git a/ocean_data_parser/parsers/dfo/odf_source/process.py b/ocean_data_parser/parsers/dfo/odf_source/process.py index c05d6bb3..e4c91b33 100644 --- a/ocean_data_parser/parsers/dfo/odf_source/process.py +++ b/ocean_data_parser/parsers/dfo/odf_source/process.py @@ -1,4 +1,5 @@ """General module use to convert ODF files into a NetCDF CF, ACDD compliant format.""" + import logging import re from pathlib import Path @@ -6,7 +7,7 @@ import xarray as xr import ocean_data_parser.parsers.seabird as seabird -from ocean_data_parser._version import __version__ +from ocean_data_parser import __version__ from ocean_data_parser.parsers.dfo.odf_source import attributes, flags from ocean_data_parser.parsers.dfo.odf_source import parser as odf_parser from ocean_data_parser.parsers.utils import standardize_dataset diff --git a/ocean_data_parser/parsers/dfo/odf_source/references/reference_platforms.csv b/ocean_data_parser/parsers/dfo/odf_source/references/reference_platforms.csv deleted file mode 100644 index 0bcdf6da..00000000 --- a/ocean_data_parser/parsers/dfo/odf_source/references/reference_platforms.csv +++ /dev/null @@ -1,87 +0,0 @@ -platform_name,platform_owner,platform_type,country_of_origin,ices_platform_codes,wmo_platform_code,call_sign,sdn_platform_urn -RRS James Cook,National Environment Research Council,Research Vessel,United Kingdom,740H,9338242,MLRM6,SDN:C17::740H -Neil Armstrong,Woods Hole Oceanographic Institution,Research Vessel,United States,33VB,9688946,WRNU,SDN:C17::33VB -Atlantis,U.S. Navy,Research Vessel,United States,33AT,9105798,KAQP,SDN:C17::33AT -Endeavor,National Science Foundation,Research Vessel,United States,32EV,7604300,WCE5063,SDN:C17::32EV -Dana,Technical University of Denmark,Research Vessel,Denmark,26D4,7912680,OXBH,SDN:C17::26D4 -Vector,Canadian Coast Guard,Mid Shore Science Vessel,Canada,18VT,6717760,CGBW,SDN:C17::18VT -Vladykov,Canadian Coast Guard,Nearshore Fishery Research Vessel,Canada,18VD,9656149,CFN5960,SDN:C17::18VD -Teleost,Canadian Coast Guard,Ice-Reinforced Factory Freeze Trawler,Canada,18TL,8714346,CGCB,SDN:C17::18TL -Sir John Franklin,Canadian Coast Guard,Offshore Fishery Science Vessel,Canada,18SZ,9781839,VFAM,SDN:C17::18SZ -Louis S. St-Laurent,Canadian Coast Guard,Heavy Icebreaker,Canada,18SN,6705937,CGBN,SDN:C17::18SN -Sigma T,Canadian Coast Guard,Coastal Research Vessel,Canada,18SG,,,SDN:C17::18SG -Neocaligus,Canadian Coast Guard,Nearshore Fishery Research Vessel,Canada,18QX,,CFG7677,SDN:C17::18QX -Coriolis II,"institutions: ISMER, UQAM, McGill University, INRS-ETE, MLI-DFO ",Research Vessel,Canada,18OL,8818570,CGDN,SDN:C17::18OL -Alfred Needler,Canadian Coast Guard,Offshore Fishery Science Vessel,Canada,18NE,7907104,CG2683,SDN:C17::18NE -A. Needler,Canadian Coast Guard,Offshore Fishery Science Vessel,Canada,18NE,7907104,CG2683,SDN:C17::18NE -M. Perley,Canadian Coast Guard,Nearshore Fishery Research,Canada,18MU,9656151,CGMP,SDN:C17::18MU -Leim,Canadian Coast Guard,Nearshore Fishery Research Vessel,Canada,18LO,9656163,CFN6223,SDN:C17::18LO -Limnos,Canadian Coast Guard,Mid Shore Science Vessel,Canada,18LN,6804903,CG2350,SDN:C17::18LN -John Cabot,Canadian Coast Guard,Offshore Fishery Science Vessel,Canada,18JC,9781853,CGDJ,SDN:C17::18JC -Hudson,Canadian Coast Guard,Offshore Research Vessel,Canada,18HU,5405279,CGDG,SDN:C17::18HU -Amundsen,Canadian Coast Guard,Research Vessel,Canada,18DL,7510846,CGDT,SDN:C17::18DL -John P. Tully,Canadian Coast Guard,Offshore Oceanographic Science Vessel,Canada,18DD,8320420,CG2958,SDN:C17::18DD -Otter Bay,Canadian Coast Guard,Mid Shore Science Vessel,Canada,18D3,,CG3262,SDN:C17::18D3 -Capt Jacques Cartier,Canadian Coast Guard,Offshore Fishery Science Vessel,Canada,18CR,9781841,CFA3098,SDN:C17::18CR -Viola M. Davidson,Canadian Coast Guard,Research vessel,Canada,18AU,,CGEC,SDN:C17::18AU -Sir Wilfrid Laurier,Canadian Coast Guard,Arctic Class 2,Canada, 18LU,8320456,CGJK,SDN:C17::18LU -Fugro Discovery,Fugro,Hydrographic Survey Vessel,Panama,PADS,9152882,,SDN:C17::PADS -Dawson,Unknown,Research Vessel,Canada,18DA,6726802,CGBV,SDN:C17::18DA -Lady Hammond,Bedford Institute of Oceanography,Research Vessel,Canada,18LH,7214844,,SDN:C17::18LH -Beluga II,Canadian Coast Guard,self-propelled boad,Canada,18BP,,,SDN:C17::18BP -Parizeau,Canadian Coast Guard,Research Vessel,Canada,18PZ,6711728,CGBS,SDN:C17::18PZ -Des Groseilliers,Canadian Coast Guard,Research Vessel,Canada,18GO,8006385,CGDX,SDN:C17::18GO -Martha L. Black,Canadian Coast Guard,Research Vessel,Canada,18MF,8320432,CCGC,SDN:C17::18MF -Sambro,Canadian Coast Guard,self-propelled small boat,Canada,18S9,,CG2613,SDN:C17::18S9 -Penguin,USS,naval vessel,United States,319P,,NUKM,SDN:C17::319P -Opilio,,,Canada,18OP,,CFD2576,SDN:C17::18OP -Earl Grey,Canadian Coast Guard,naval vessel,Canada,18EG,8412340,CG3029,SDN:C17::18EG -Spray,Canadian Coast Guard,self-propelled small boat,Canada,18PY,,CG2248,SDN:C17::18PY -Cap Breton,Canadian Coast Guard,ship,Canada,18BO,,,SDN:C17::18BO -Fundy Spray,HMSC,research vessel,Canada,18FQ,,,SDN:C17::18FQ -Sir William Alexander,Canadian Coast Guard,vessel of opportunity,Canada,18WA,8320482,CGUM,SDN:C17:18WA -Cutter 141,,,,,,, -Shippigan,,,,,,, -WB Scott,,,,,,, -Cap Nord,,,,,,, -Pandaslus,,,,,,, -E.E.Prince,,,,,,, -C6-4828,,,,,,, -JL HART,,,,,,, -VESSEL OF OPPORTUNITY,,,,,,, -Pack_Kat,,,,,,, -Pack_Kat Launch,,,,,,, -Dalhousie University Zodiac,,,,,,, -C1196NS ROSBOROUGH,,,,,,, -ABCO (Aluminum Boat Company),,,,,,, -MESD LAUNCH,,,,,,, -WILFRED TEMPLEMAN,Canadian Coast Guard,research vessel,Canada,181C,7907099,CGDV,SDN:C17:181C -Henry Larsen,Canadian Coast Guard,vessel of opportunity,Canada,18HS,8409329,CGHL,SDN:C17::18HS -Pierre Radisson,Canadian Coast Guard,research vessel,Canada,18RD,7510834,CGSB,SDN:C17:18RD -Sackville,Fisheries Research Board,research vessel,Canada,18SV,,CNAV,SDN:C17::18SV -Unknown, -Navicula,Canadian Coast Guard,research vessel,Canada,18NA,,CG2364,SDN:C17::18NA -Pandora II,,,Canada,18P2,,CZ3946,SDN:C17::18P2 -Ibis,,,,,,, -Helicopter,,,,,,, -Edward Cornwallis -Quest -Cygnus -Quadra,Canadian Coast Guard,research vessel,Canada,18QA,,VE0MP,SDN:C17::18QA -Adolf Jensen -Narwhal -Tudlik -Petrel,,naval vessel,France,35PE,,FCPL,SDN:C17::35PE -Maxwell,Canadian Coast Guard,research vessel,Canada,18MX,5230040,,SDN:C17::18MX -Theron,,research vessel,Canada,18TE,5358737,,SDN:C17::18TE -Baffin,,research vessel,Canada,18BA,,CGCL,SDN:C17::18BA -Tulugaq -Fort Francis, -Provo Wallis,,,,,,CGDP,SDN:C17::18PW -IPMF Barge, -HARLEQUIN, -MULTIPLE SHIPS, -BOUEE OCEANOGRAPHIQUE, -Variable, -NSC F.G. CREED,Canadian Coast Guard,research vessel,Canada,18FC,8944496,CG3198,SDN:C17::18FC -Macoma,,,Denmark,26MK,,XPA4682,SDN:C17::26MK diff --git a/ocean_data_parser/parsers/electricblue.py b/ocean_data_parser/parsers/electricblue.py index 36270d5b..63e480c2 100644 --- a/ocean_data_parser/parsers/electricblue.py +++ b/ocean_data_parser/parsers/electricblue.py @@ -1,9 +1,9 @@ """ -# ElectricBlue - - -ElectricBlue is a non-profit technology transfer startup creating research-oriented solutions for the scientific community +[ElectricBlue](https://electricblue.eu/envloggers) +is a non-profit technology transfer startup creating +research-oriented solutions for the scientific community. """ + import logging import re @@ -17,13 +17,13 @@ logger = logging.getLogger(__name__) -default_global_attribute = { +GLOBAL_ATTRIBUTES = { "instrument_manufacturer": "ElectricBlue", "instrument_manufacturer_webpage": "https://electricblue.eu/", "source": None, "source_file_header": "", } -default_variable_attributes = { +VARIABLE_ATTRIBUTES = { "latitude": { "long_name": "Latitude", "units": "degrees_east", @@ -48,12 +48,13 @@ def csv( Args: path (str): path to the csv file to parse encoding (str='UTF-8', optional): file encoding + Returns: - dataset: xarray dataset + xarray.Dataset """ with open(path, encoding=encoding) as f: line = True - metadata = default_global_attribute + metadata = GLOBAL_ATTRIBUTES metadata["source_file"] = path while line: @@ -70,7 +71,9 @@ def csv( key = items[0] value = items[1] if len(items) == 2 else "" - attr = re.sub(r"[\s\[\]\(\)]+", "_", key.lower()) + attr = re.sub(r"[\s\[\]\(\)\-]+", "_", key.lower()) + attr = re.sub(r"__+", "_", attr) + attr = re.sub(r"_$", "", attr) # cast value if re.match(r"^[+-]*\d+$", value): @@ -112,8 +115,8 @@ def csv( # Variables attributes for var in ds: - if var in default_variable_attributes: - ds[var].attrs = default_variable_attributes[var] + if var in VARIABLE_ATTRIBUTES: + ds[var].attrs = VARIABLE_ATTRIBUTES[var] ds["temp"].attrs["units"] = ds.attrs.pop("temperature") ds = standardize_dataset(ds) return ds @@ -122,10 +125,22 @@ def csv( def log_csv( path: str, encoding: str = "UTF-8", rename_variables: bool = True ) -> xarray.Dataset: + """Parse ElectricBlue log csv file + + Args: + path (str): path to the csv file + encoding (str, optional): File encoding. Defaults to "UTF-8". + rename_variables (bool, optional): Rename variables to + valid NetCDF names. Defaults to True. + + Returns: + xarray.Dataset + """ + df = pd.read_csv(path, encoding=encoding, parse_dates=True, index_col=["time"]) ds = df.to_xarray() # add default attributes - ds.attrs.update({**default_global_attribute, "source": path}) + ds.attrs.update({**GLOBAL_ATTRIBUTES, "source": path}) ds = standardize_dataset(ds) # Rename variables to be compatible with NetCDF diff --git a/ocean_data_parser/parsers/nmea.py b/ocean_data_parser/parsers/nmea.py index b63acda9..4534dbce 100644 --- a/ocean_data_parser/parsers/nmea.py +++ b/ocean_data_parser/parsers/nmea.py @@ -1,8 +1,7 @@ """ -# NMEA Standard Protocol - - - +The NMEA 0183 protocol is a standard communication protocol used in marine +and navigation systems to exchange data between different electronic devices. +It stands for "National Marine Electronics Association 0183." """ import logging @@ -15,14 +14,8 @@ logger = logging.getLogger(__name__) -variable_mapping = { - ("Heave", "heading"): ( - "Heave", - "heave", - ) # fix in https://github.com/Knio/pynmea2/pull/129 but not included in pipy yet -} -nmea_dtype_mapping = { +NMEA_0183_DTYPES = { "row": float, "prefix": str, "talker": str, @@ -61,7 +54,7 @@ "heading": float, "heading_true": float, "heading_magnetic": float, - "" "hdg_true": str, + "hdg_true": str, "wind_angle": float, "reference": str, "wind_speed": float, @@ -179,7 +172,11 @@ def _generate_extra_terms(nmea): f"{nmea['year']}-{nmea['month']}-{nmea['day']}T{nmea['timestamp']} UTC", f"%Y-%m-%dT%H%M%S{'.%f' if len(nmea['timestamp'])>6 else''} %Z", ) - if nmea["sentence_type"] == "RMC": + if ( + nmea["sentence_type"] == "RMC" + and nmea.get("timestamp") + and nmea.get("datestamp") + ): extra[("GPS Time", "gps_datetime")] = datetime.strptime( f"{nmea['datestamp']}T{nmea['timestamp']} UTC", f"%d%m%yT%H%M%S{'.%f' if len(nmea['timestamp'])>6 else''} %Z", @@ -231,7 +228,10 @@ def nmea_0183( def rename_variable(name): """Rename variable based on variable mapping dictionary or return name""" - return variable_mapping[name] if name in variable_mapping else name + if name == ("Heave", "heading"): + # fix in https://github.com/Knio/pynmea2/pull/129 but not included in pipy yet + return ("Heave", "heave") + return name nmea = [] long_names = {} @@ -284,22 +284,20 @@ def rename_variable(name): df = df.astype( { var: dtype - for var, dtype in nmea_dtype_mapping.items() + for var, dtype in NMEA_0183_DTYPES.items() if var in df and dtype != datetime } ) # Cast variables to the appropriate type - unknown_variables_dtype = [var for var in df if var not in nmea_dtype_mapping] + unknown_variables_dtype = [var for var in df if var not in NMEA_0183_DTYPES] if unknown_variables_dtype: logger.warning("unknown dtype for nmea columns: %s", unknown_variables_dtype) # Convert datetime columns for col in df: - if nmea_dtype_mapping.get(col) != datetime: + if NMEA_0183_DTYPES.get(col) != datetime: continue - df[col] = ( - pd.to_datetime(df[col], utc=True).dt.tz_convert(None).dt.to_pydatetime() - ) + df[col] = pd.to_datetime(df[col], utc=True).dt.tz_convert(None) df = df.replace({np.nan: None, "": None, "None": None}) diff --git a/ocean_data_parser/parsers/onset.py b/ocean_data_parser/parsers/onset.py index da12c45a..1ec21ad6 100644 --- a/ocean_data_parser/parsers/onset.py +++ b/ocean_data_parser/parsers/onset.py @@ -1,26 +1,28 @@ -""" -# Onset - - """ +[Onset](https://www.onsetcomp.com/) is a company that manufactures +data loggers and sensors for environmental monitoring. +Their Hobo data loggers are widely used for monitoring water +quality parameters such as temperature, conductivity, and light +intensity. The present module provides a parser for the CSV files +generated by the HOBOware software. +""" + import logging import re from csv import reader from datetime import datetime -from typing import Union -import numpy as np import pandas as pd import xarray -from dateutil.parser._parser import ParserError from ocean_data_parser.parsers.utils import standardize_dataset -global_attributes = {"Convention": "CF-1.6"} +GLOBAL_ATTRIBUTES = {"Convention": "CF-1.6"} logger = logging.getLogger(__name__) -_onset_variables_mapping = { +VARIABLE_NAME_MAPPING = { "#": "record_number", + "\ufeff#": "record_number", "Date Time": "time", "Temp": "temperature", "Intensity": "light_intensity", @@ -36,7 +38,7 @@ "Water Level": "water_level", } -_ignored_variables = [ +IGNORED_VARIABLES = [ "record_number", "time", "button_up", @@ -54,49 +56,33 @@ "low_power", "water_detect", "record", + "eof", "", ] -datetime_regex_to_formats = [ +DATETIME_REGEX_FORMATS = [ (r"\d\d\/\d\d\/\d\d\s+\d\d\:\d\d\:\d\d\s+\w\w", r"%m/%d/%y %I:%M:%S %p"), (r"\d\d\d\d\/\d\d\/\d\d\s+\d\d\:\d\d\:\d\d\s+\w\w", r"%Y/%m/%d %I:%M:%S %p"), (r"\d\d\/\d\d\/\d\d\s+\d\d\:\d\d", r"%m/%d/%y %H:%M"), + (r"\d\d\/\d\d\/\d\d\s+\d\d\:\d\d:\d\d", r"%y/%m/%d %H:%M:%S"), + (r"\d\d\/\d\d\/\d\d\d\d\s+\d\d\:\d\d\:\d\d\s+(AM|PM)", r"%m/%d/%Y %H:%M:%S %p"), (r"\d+\/\d+\/\d\d\s+\d\d\:\d\d", r"%m/%d/%y %H:%M"), (r"^\d\d\d\d\-\d\d\-\d\d\s+\d\d\:\d\d\:\d\d$", r"%Y-%m-%d %H:%M:%S"), (r"\d\d\d\d\-\d\d\-\d\d\s+\d\d\:\d\d\:\d\d (AM|PM)", r"%Y-%m-%d %I:%M:%S %p"), (r"^\d\d\-\d\d\-\d\d\s+\d{1,2}\:\d\d$", r"%y-%m-%d %H:%M"), (r"^\d\d\-\d\d\-\d\d\s+\d{1,2}\:\d\d\:\d\d$", r"%y-%m-%d %H:%M:%S"), (r"^\d\d\d\d\-\d\d\-\d\d\s+\d{1,2}\:\d\d$", r"%Y-%m-%d %H:%M"), - (r"^\d\d\/\d\d\/\d\d\d\d\s\d\d\:\d\d", r"%m/%d/%Y %H:%M"), + (r"^\d{1,2}\/\d{1,2}\/\d\d\d\d\s\d{1,2}\:\d\d", r"%m/%d/%Y %H:%M"), + (r"^\d\d\d\d\-\d\d\-\d\d\s+\d{1,2}:\d\d:\d\d (AM|PM)", r"%Y-%m-%d %I:%M:%S %p"), ] -def _parse_onset_time( - time: Union[str, np.datetime64], timezone: str = "UTC" -) -> pd.Timestamp: - """Convert Onset timestamps to UTC pd.Timestamps""" - if time in ("", None) or pd.isna(time): - return pd.NaT - - if isinstance(time, np.datetime64): - time_format = None - else: - for regex, datetime_format in datetime_regex_to_formats: - if re.match(regex, time): - time_format = datetime_format - break - else: - logger.error("Unkown datetime format: %s", time) - return pd.NaT - try: - return ( - pd.to_datetime(time, format=time_format) - .tz_localize(timezone) - .tz_convert("UTC") - ) - except ParserError: - logging.error("Failed to convert to timestamp: %s", time, exc_info=True) - return pd.NaT +def _get_time_format(time): + for regex, datetime_format in DATETIME_REGEX_FORMATS: + if re.fullmatch(regex, time): + return datetime_format + logger.warning("Unknown datetime format: %s", time) + return None def _parse_onset_csv_header(header_lines): @@ -155,8 +141,8 @@ def _parse_onset_csv_header(header_lines): def _standardized_variable_mapping(variables): """Standardize onset variable names""" return { - var: _onset_variables_mapping[var] - if var in _onset_variables_mapping + var: VARIABLE_NAME_MAPPING[var] + if var in VARIABLE_NAME_MAPPING else var.lower().replace(" ", "_") for var in variables } @@ -165,59 +151,77 @@ def _standardized_variable_mapping(variables): def csv( path: str, convert_units_to_si: bool = True, - read_csv_kwargs: dict = None, standardize_variable_names: bool = True, + encoding: str = "UTF-8", + errors: str = "strict", ) -> xarray.Dataset: """Parses the Onset CSV format generate by HOBOware into a xarray object + Inputs: path: The path to the CSV file convert_units_to_si: Whether to standardize data units to SI units - read_csv_kwargs: dictionary of keyword arguments to be passed to pd.read_csv standardize_variable_names: Rename the variable names a standardize name convention + encoding: File encoding. Defaults to "utf-8" + errors: Error handling. Defaults to "strict" Returns: xarray.Dataset """ - if read_csv_kwargs is None: - read_csv_kwargs = {} + raw_header = [] + line = "" with open( path, - encoding=read_csv_kwargs.get("encoding", "UTF-8"), - errors=read_csv_kwargs.get("encoding_errors"), + encoding=encoding, + errors=errors, ) as f: - raw_header += [f.readline().replace("\n", "")] - header_lines = 1 - if "Serial Number:" in raw_header[0]: - # skip second empty line - header_lines += 1 - f.readline() # - # Read csv columns - raw_header += [f.readline()] + while "Date Time" not in line and len(raw_header) < 10: + line = f.readline() + raw_header.append(line) + first_row = f.readline() + if "Date Time" not in raw_header[-1]: + raise ValueError("Date Time column not found in header") # Parse onset header header, variables = _parse_onset_csv_header(raw_header) + date_column_index = list(variables.keys()).index("Date Time") + date_format = _get_time_format(first_row.split(",")[date_column_index]) # Inputs to pd.read_csv - column_names = [var for var in list(variables.keys()) if var] + consider_columns = { + var: id + for id, var in enumerate(variables.keys()) + if var.lower().replace(" ", "_") not in IGNORED_VARIABLES + } df = pd.read_csv( path, na_values=[" "], + skiprows=list(range(len(raw_header))), + parse_dates=["Date Time"], + date_format=date_format, sep=",", - engine="python", - header=header_lines, + header=None, memory_map=True, - names=column_names, - usecols=[id for id, name in enumerate(column_names)], - **read_csv_kwargs, - ) - df[header["time_variables"]] = df[header["time_variables"]].applymap( - lambda x: _parse_onset_time(x, header["timezone"]) + names=consider_columns.keys(), + usecols=consider_columns.values(), + encoding_errors=errors, + encoding=encoding, ) + # Add timezone to time variables + if df["Date Time"].dtype == "object": + logger.warning( + "Date Time column is not in a consistent format. Trying to convert" + ) + df["Date Time"] = df["Date Time"].apply( + lambda x: pd.to_datetime(x, format=_get_time_format(x)) + ) + + df["Date Time"] = df["Date Time"].dt.tz_localize(header["timezone"]) + # Convert to dataset ds = df.to_xarray() - ds.attrs = {**global_attributes, **header} + ds.attrs = {**GLOBAL_ATTRIBUTES, **header} for var in ds: ds[var].attrs = variables[var] @@ -230,7 +234,7 @@ def csv( if convert_units_to_si: if standardize_variable_names: if "temperature" in ds and ("C" not in ds["temperature"].attrs["units"]): - logger.warning("Temperaure in farenheit will be converted to celsius") + logger.warning("Temperature in Farenheit will be converted to celsius") ds["temperature"] = _farenheit_to_celsius(ds["temperature"]) ds["temperature"].attrs["units"] = "degC" ds.attrs["history"] += " ".join( @@ -288,7 +292,7 @@ def _detect_instrument_type(ds): vars_of_interest = { var for var in ds - if var not in _ignored_variables and not var.startswith("unnamed") + if var not in IGNORED_VARIABLES and not var.startswith("unnamed") } if vars_of_interest == {"temperature", "light_intensity"}: diff --git a/ocean_data_parser/parsers/pme.py b/ocean_data_parser/parsers/pme.py index 19ea9e90..f2a023e1 100644 --- a/ocean_data_parser/parsers/pme.py +++ b/ocean_data_parser/parsers/pme.py @@ -1,13 +1,11 @@ """ -# PME Instruments - - +[Precision Measurement Engineering (PME)](https://www.pme.com/) +is a company that manufactures instruments to measure different water properties. """ import logging import re import warnings -from datetime import datetime from typing import Union import pandas as pd @@ -17,7 +15,7 @@ from ocean_data_parser.parsers.utils import standardize_dataset logger = logging.getLogger(__name__) -variable_attributes = { +VARIABLE_ATTRIBUTES = { "index": {}, "Time (sec)": {"long_name": "Time", "standard_name": "time"}, "T (deg C)": { @@ -46,53 +44,47 @@ "Q ()": {"long_name": "Q"}, } -vars_rename = { +VARIABLE_RENAMING_MAPPING = { "Time (sec)": "time", "T (deg C)": "temperature", "BV (Volts)": "batt_volt", "DO (mg/l)": "do_mg_l", + "DO (perc)": "do_perc", + "pO2 (mbar)": "po2_mbar", "Q ()": "q", } -variable_attributes = { - "Time (sec)": dict(long_name="Time", standard_name="time"), - "T (deg C)": dict( - long_name="Temperature", - units="degrees_celsius", - standard_name="sea_water_temperature", - ), - "BV (Volts)": dict(long_name="Battery Voltage", units="Volts"), - "DO (mg/l)": dict( - long_name="Dissolved Oxygen Concentration", - units="mg/l", - standard_name="mass_concentration_of_oxygen_in_sea_water", - ), - "Q ()": dict(long_name="Q"), -} - global_attributes = {"Conventions": "CF-1.6"} def minidot_txt( - path: str, read_csv_kwargs: dict = None, rename_variables: bool = True + path: str, + rename_variables: bool = True, + encoding: str = "utf-8", + errors: str = "strict", + timezone: str = "UTC", ) -> xr.Dataset: - """ - minidot_txt parses the txt format provided by the PME Minidot instruments. + """Parse PME MiniDot txt file + + Args: + path (str): txt file path to read + rename_variables (bool, optional): _description_. Defaults to True. + encoding (str, optional): File encoding. Defaults to 'utf-8'. + errors (str, optional): Error handling. Defaults to 'strict'. + + Returns: + xarray.Dataset """ def _append_to_history(msg): ds.attrs["history"] += f"{pd.Timestamp.utcnow():%Y-%m-%dT%H:%M:%SZ} {msg}" - # Default read_csv_kwargs - if read_csv_kwargs is None: - read_csv_kwargs = {} - # Read MiniDot with open( path, "r", - encoding=read_csv_kwargs.get("encoding", "utf-8"), - errors=read_csv_kwargs.get("encoding_errors"), + encoding=encoding, + errors=errors, ) as f: # Read the headre serial_number = f.readline().replace("\n", "") @@ -110,15 +102,25 @@ def _append_to_history(msg): warnings.warn("Failed to read: {path}", RuntimeWarning) return pd.DataFrame(), None + # Parse column names + columns = [item.strip() for item in f.readline().split(",")] + # Read the data with pandas - ds = pd.read_csv( + df = pd.read_csv( f, - converters={0: lambda x: pd.Timestamp(int(x), unit="s", tz="UTC")}, - **read_csv_kwargs, - ).to_xarray() + converters={0: lambda x: pd.Timestamp(int(x), unit="s")}, + encoding=encoding, + encoding_errors=errors, + names=columns, + header=None, + ) + ds = df.to_xarray() - # Strip whitespaces from variables names - ds = ds.rename({var: var.strip() for var in ds.keys()}) + ds["Time (sec)"] = ( + ds.dims, + ds["Time (sec)"].to_index().tz_localize(timezone), + {"timezone": timezone}, + ) # Global attributes ds.attrs = { @@ -151,32 +153,36 @@ def _append_to_history(msg): # Add attributes to the dataset and rename variables to mapped names. for var in ds.variables: - if var not in variable_attributes: + if var not in VARIABLE_ATTRIBUTES: logger.warning("Unknown variable: %s", var) continue - ds[var].attrs = variable_attributes[var] + ds[var].attrs.update(VARIABLE_ATTRIBUTES[var]) if rename_variables: - ds = ds.rename_vars(vars_rename) + ds = ds.rename_vars(VARIABLE_RENAMING_MAPPING) ds.attrs[ "history" - ] += f"\n{pd.Timestamp.now().isoformat()} Rename variables: {vars_rename}" + ] += f"\n{pd.Timestamp.now().isoformat()} Rename variables: {VARIABLE_RENAMING_MAPPING}" ds = standardize_dataset(ds) return ds -def minidot_txts(paths: Union[list, str]) -> xr.Dataset: +def minidot_txts( + paths: Union[list, str], encoding: str = "utf-8", errors: str = "strict" +) -> xr.Dataset: """Parse PME Minidots txt files Args: paths (listorstr): List of file paths to read. + encoding (str, optional): File encoding. Defaults to 'utf-8'. + errors (str, optional): Error handling. Defaults to 'strict'. Returns: xr.Dataset: xarray dataset which is compliant with CF-1.6 """ # If a single string is givien, assume only one path - if type(paths) is str: + if isinstance(paths, str): paths = [paths] datasets = [] @@ -186,18 +192,25 @@ def minidot_txts(paths: Union[list, str]) -> xr.Dataset: print(f"Ignore {path}") continue # Read txt file - datasets += minidot_txt(path) + datasets += minidot_txt(path, encoding=encoding, errors=errors) return xr.merge(datasets) -def minidot_cat(path: str, read_csv_kwargs: dict = None) -> xr.Dataset: - """ - cat reads PME MiniDot concatenated CAT files +def minidot_cat( + path: str, encoding: str = "utf-8", errors: str = "strict" +) -> xr.Dataset: + """cat reads PME MiniDot concatenated CAT files + + Args: + path (str): File path to read + encoding (str, optional): File encoding. Defaults to 'utf-8'. + errors (str, optional): Error handling. Defaults to 'strict'. + + Returns: + xr.Dataset: xarray dataset which is compliant with CF-1.6 """ - if read_csv_kwargs is None: - read_csv_kwargs = {} - with open(path, "r", encoding=read_csv_kwargs.get("encoding", "utf8")) as f: + with open(path, "r", encoding=encoding, errors=errors) as f: header = f.readline() if header != "MiniDOT Logger Concatenated Data File\n": @@ -211,7 +224,9 @@ def minidot_cat(path: str, read_csv_kwargs: dict = None) -> xr.Dataset: names = columns[0].replace(r"\n", "").split(",") units = columns[1].replace(r"\n", "") - ds = pd.read_csv(f, names=names, **read_csv_kwargs).to_xarray() + ds = pd.read_csv( + f, names=names, encoding=encoding, encoding_errors=errors + ).to_xarray() # Include units for name, units in zip(names, units): diff --git a/ocean_data_parser/parsers/rbr.py b/ocean_data_parser/parsers/rbr.py index 657ed695..a5b38120 100644 --- a/ocean_data_parser/parsers/rbr.py +++ b/ocean_data_parser/parsers/rbr.py @@ -1,31 +1,43 @@ """ -# RBR Ltd. - +RBR Ltd. is a company that specializes in oceanographic instruments and sensors. +They provide a range of instruments for measuring various parameters in the ocean, +including temperature, salinity, pressure, and more. """ + import re import pandas as pd +from loguru import logger +from xarray import Dataset from ocean_data_parser.parsers.utils import standardize_dataset -def rtext(file_path, encoding="UTF-8", output=None): - """ - Read RBR R-Text format. - :param errors: default ignore - :param encoding: default UTF-8 - :param file_path: path to file to read - :return: metadata dictionary dataframe - """ - # MON File Header end - header_end = "NumberOfSamples" +def rtext( + file_path: str, + encoding="UTF-8", + header_end: str = "NumberOfSamples", + errors: str = "raise", +) -> Dataset: + """Read RBR legacy R-Text Engineering format. - with open(file_path, encoding=encoding) as fid: - line = "" - section = "header_info" - metadata = {section: {}} + Args: + file_path (Path): RBR R-Text file path + encoding (str, optional): File encoding. Defaults to "UTF-8". + header_end (str, optional): End of the metadata header. + Defaults to "NumberOfSamples". + errors (str, optional): Error handling. Defaults to "raise". + Raises: + RuntimeError: File length do not match expected Number of Samples + + Returns: + Dataset: Parsed Dataset + """ + line = "" + metadata = {} + with open(file_path, encoding=encoding) as fid: while not line.startswith(header_end): # Read line by line line = fid.readline() @@ -61,7 +73,12 @@ def rtext(file_path, encoding="UTF-8", output=None): # Make sure that line count is good if ds.dims["index"] != metadata["number_of_samples"]: - raise RuntimeError("Data length do not match expected Number of Samples") + if errors == "raise": + raise RuntimeError( + "Data length do not match expected Number of Samples" + ) + else: + logger.warning("Data length do not match expected Number of Samples") # Convert to datset ds.attrs = { diff --git a/ocean_data_parser/parsers/seabird.py b/ocean_data_parser/parsers/seabird.py index da700f9a..ba650bf6 100644 --- a/ocean_data_parser/parsers/seabird.py +++ b/ocean_data_parser/parsers/seabird.py @@ -1,12 +1,12 @@ """ -This module contains all the different tools used to handle the different [Seabird Scientific](https://www.seabird.com) file formats. - +This page provides functions for parsing data files in Seabird Scientific format. +The Seabird Scientific format is commonly used for oceanographic data collection +and is supported by [Seabird Scientific](https://www.seabird.com). """ import difflib import json import logging -import os import re from datetime import datetime @@ -18,25 +18,62 @@ from ocean_data_parser.parsers.utils import convert_datetime_str, standardize_dataset from ocean_data_parser.vocabularies.load import seabird_vocabulary -SBE_TIME_FORMAT = "%b %d %Y %H:%M:%S" # Jun 23 2016 13:51:30 +logger = logging.getLogger(__name__) + var_dtypes = { "date": str, "bottle": str, - "Flag": int, - "flag": int, "stats": str, "scan": int, } +SBE_TIME_FORMAT = "%b %d %Y %H:%M:%S" # Jun 23 2016 13:51:30 sbe_time = re.compile( r"(?P