Skip to content

Commit

Permalink
WIP: furuno
Browse files Browse the repository at this point in the history
  • Loading branch information
kmuehlbauer committed Sep 27, 2022
1 parent f8a5a6c commit dd7418c
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 47 deletions.
4 changes: 4 additions & 0 deletions docs/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 0.7.0 (2022-09-21)

* add Furuno SCN/SCNX importer ({pull}`30`) by [@kmuehlbauer](https://github.com/kmuehlbauer)

## 0.7.0 (2022-09-21)

* Add zenodo badges to README.md ({pull}`22`) by [@mgrover1](https://github.com/mgrover1)
* Fix version on RTD ({pull}`23`) by [@kmuehlbauer](https://github.com/kmuehlbauer)
* Add minimal documentation for Datamodel ({pull}`24`) by [@kmuehlbauer](https://github.com/kmuehlbauer)
Expand Down
18 changes: 18 additions & 0 deletions docs/importers.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,21 @@ more functions are applied on that {py:class}`xarray:xarray.Dataset`.
With {class}`xradar.io.backends.odim.open_odim_datatree` all groups (eg. ``datasetN``)
are extracted. From that the ``root`` group is processed. Everything is finally added as
ParentNodes and ChildNodes to a {py:class}`datatree:datatree.Datatree`.


## Furuno SCN and SCNX

### FurunoBackendEntrypoint

The xarray backend {class}`xradar.io.backends.furuno.FurunoBackendEntrypoint`
opens the file with {class}`xradar.io.backends.furuno.FurunoStore`.
Furuno SCN and SCNX data files contain only one one sweep group, so the
group-keyword isn't used. Several private helper functions are used to
conveniently access data and metadata. Finally, the xarray machinery returns
a {py:class}`xarray:xarray.Dataset` with the sweep group.

### open_furuno_datatree

With {class}`xradar.io.backends.furuno.open_furuno_datatree` the single group
is extracted. From that the ``root`` group is processed. Everything is finally
added as ParentNodes and ChildNodes to a {py:class}`datatree:datatree.Datatree`.
1 change: 1 addition & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ notebooks/CfRadial1
notebooks/CfRadial1_full
notebooks/ODIM_H5
notebooks/ODIM_H5_full
notebooks/Furuno
```
209 changes: 209 additions & 0 deletions examples/notebooks/Furuno.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "59447ad6-ac47-494e-b696-4335b36b205b",
"metadata": {
"tags": []
},
"source": [
"# Furuno - Simple"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6f96b5d8-2b96-4fd7-b8ba-166c34a8dcd2",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import xarray as xr\n",
"import xradar as xd\n",
"from urllib.parse import urljoin\n",
"from urllib.request import urlretrieve"
]
},
{
"cell_type": "markdown",
"id": "33d50be4-dfe5-4d99-a936-67a9a76bac94",
"metadata": {},
"source": [
"## Download\n",
"\n",
"Fetching Furuno radar data file from wradlib-data repository."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d3c6d408-5ab2-43c3-afd1-b3a703ef3b24",
"metadata": {},
"outputs": [],
"source": [
"def fetch_furuno_file():\n",
" fname = \"furuno_data.scn.gz\"\n",
" if not os.path.exists(fname):\n",
" base_url = \"https://raw.githubusercontent.com/wradlib/wradlib-data/main/furuno/\"\n",
" #filename = \"2006_20220324_000000_000.scnx.gz\"\n",
" filename = \"0080_20210730_160000_01_02.scn.gz\"\n",
" url = urljoin(base_url, filename)\n",
" urlretrieve(url, filename=fname)\n",
" return fname\n",
"\n",
"\n",
"filename = fetch_furuno_file()"
]
},
{
"cell_type": "markdown",
"id": "b987dcfd-5105-4483-932e-71b8002e5f09",
"metadata": {},
"source": [
"## xr.open_dataset\n",
"\n",
"Making use of the xarray `odim` backend. We also need to provide the group."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7675b518-18e4-4ea6-b101-f1bccf603902",
"metadata": {},
"outputs": [],
"source": [
"ds = xr.open_dataset(\n",
" filename, engine=\"furuno\", backend_kwargs=dict(reindex_angle=False)\n",
")\n",
"display(ds)"
]
},
{
"cell_type": "markdown",
"id": "01ec8c90-2da8-46ae-a0b5-0e1792a79bbe",
"metadata": {},
"source": [
"### Plot Time vs. Azimuth"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "24dfb5f8-de27-46f0-9246-722e5955a345",
"metadata": {},
"outputs": [],
"source": [
"ds.azimuth.plot()"
]
},
{
"cell_type": "markdown",
"id": "222ca704-d6e1-49e6-84a0-de55df9fdf61",
"metadata": {},
"source": [
"### Plot Range vs. Time"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "34429720-e689-4786-99e3-5af9742d19ad",
"metadata": {},
"outputs": [],
"source": [
"ds.DBZH.plot()"
]
},
{
"cell_type": "markdown",
"id": "d904cd09-8590-42e2-8dce-41d3949d313c",
"metadata": {},
"source": [
"### Plot Range vs. Azimuth\n",
"\n",
"We need to sort by azimuth and specify the y-coordinate."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6479a374-25ab-42be-b53e-82849b6faffc",
"metadata": {},
"outputs": [],
"source": [
"ds.DBZH.sortby(\"azimuth\").plot(y=\"azimuth\")"
]
},
{
"cell_type": "markdown",
"id": "5deafdf1-9224-459c-8b10-7c501ae13234",
"metadata": {},
"source": [
"## open_odim_datatree"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a65f0b48-7197-4f79-bc26-2045cfc59a4a",
"metadata": {},
"outputs": [],
"source": [
"dtree = xd.io.open_furuno_datatree(filename)\n",
"display(dtree)"
]
},
{
"cell_type": "markdown",
"id": "638bc6c0-3293-4661-a5d5-e9aaad14ffe9",
"metadata": {},
"source": [
"### Plot Sweep Range vs. Time"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f9db8500-7072-451b-84a1-f36767110e16",
"metadata": {},
"outputs": [],
"source": [
"dtree[\"sweep_0\"].ds.DBZH.plot()"
]
},
{
"cell_type": "markdown",
"id": "1e44c0fd-02bc-4d2d-ac91-772bdcebe04b",
"metadata": {},
"source": [
"### Plot Sweep Range vs. Azimuth"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c7aab6b3-aeb0-4ed1-8397-8b505e63464a",
"metadata": {},
"outputs": [],
"source": [
"dtree[\"sweep_0\"].ds.DBZH.sortby(\"azimuth\").plot(y=\"azimuth\")"
]
}
],
"metadata": {
"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.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
22 changes: 22 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,25 @@ def odim_file(tmp_path_factory):
fname = os.path.join(fn, "odim_data.h5")
urlretrieve(url, filename=fname)
return fname


@pytest.fixture(scope="session")
def furuno_scn_file(tmp_path_factory):
base_url = "https://raw.githubusercontent.com/wradlib/wradlib-data/main/furuno/"
filename = "0080_20210730_160000_01_02.scn.gz"
url = urljoin(base_url, filename)
fn = tmp_path_factory.mktemp("furuno_data")
fname = os.path.join(fn, "furuno_data.scn.gz")
urlretrieve(url, filename=fname)
return fname


@pytest.fixture(scope="session")
def furuno_scnx_file(tmp_path_factory):
base_url = "https://raw.githubusercontent.com/wradlib/wradlib-data/main/furuno/"
filename = "2006_20220324_000000_000.scnx.gz"
url = urljoin(base_url, filename)
fn = tmp_path_factory.mktemp("furuno_data")
fname = os.path.join(fn, "furuno_data.scnx.gz")
urlretrieve(url, filename=fname)
return fname
34 changes: 34 additions & 0 deletions tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,37 @@ def test_open_odim_dataset(odim_file):
backend_kwargs=dict(first_dim="auto"),
)
assert dict(ds.dims) == {"azimuth": 360, "range": 280}


def test_open_furuno_scn_dataset(furuno_scn_file):
# open sweep group
ds = xr.open_dataset(furuno_scn_file, engine="furuno")
assert dict(ds.dims) == {"time": 1385, "range": 602}
assert set(ds.data_vars) & (
sweep_dataset_vars | non_standard_sweep_dataset_vars
) == {"KDP", "VRADH", "ZDR", "DBZH", "WRADH", "RHOHV", "PHIDP"}

# open sweep group, auto
ds = xr.open_dataset(
furuno_scn_file,
engine="furuno",
backend_kwargs=dict(first_dim="auto"),
)
assert dict(ds.dims) == {"azimuth": 1385, "range": 602}


def test_open_furuno_scnx_dataset(furuno_scnx_file):
# open sweep group
ds = xr.open_dataset(furuno_scnx_file, engine="furuno")
assert dict(ds.dims) == {"time": 720, "range": 936}
assert set(ds.data_vars) & (
sweep_dataset_vars | non_standard_sweep_dataset_vars
) == {"KDP", "VRADH", "ZDR", "DBZH", "WRADH", "RHOHV", "PHIDP"}

# open sweep group, auto
ds = xr.open_dataset(
furuno_scnx_file,
engine="furuno",
backend_kwargs=dict(first_dim="auto"),
)
assert dict(ds.dims) == {"azimuth": 720, "range": 936}
4 changes: 2 additions & 2 deletions xradar/io/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
:maxdepth: 4
.. automodule:: xradar.io.backends.cfradial1
.. automodule:: xradar.io.backends.furuno
.. automodule:: xradar.io.backends.odim
.. automodule:: xradar.io.backends.furuno
"""

from .cfradial1 import * # noqa
from .odim import * # noqa
from .furuno import * # noqa
from .odim import * # noqa

__all__ = [s for s in dir() if not s.startswith("_")]
13 changes: 5 additions & 8 deletions xradar/io/backends/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
"""

import struct
from collections import OrderedDict

import numpy as np
import xarray as xr
from datatree import DataTree

import struct
from collections import OrderedDict


def _maybe_decode(attr):
try:
Expand Down Expand Up @@ -178,7 +178,7 @@ def _assign_root(sweeps):

# assign root attributes
attrs = {}
attrs["Conventions"] = sweeps[0].attrs["Conventions"]
attrs["Conventions"] = sweeps[0].attrs.get("Conventions", "None")
attrs.update(
{
"version": "None",
Expand All @@ -197,7 +197,6 @@ def _assign_root(sweeps):
return root



def _get_fmt_string(dictionary, retsub=False, byte_order="<"):
"""Get Format String from given dictionary.
Expand Down Expand Up @@ -286,11 +285,9 @@ def _unpack_dictionary(buffer, dictionary, rawdata=False):
# IRIS Data Types and corresponding python struct format characters
# 4.2 Scalar Definitions, Page 23
# https://docs.python.org/3/library/struct.html#format-characters
# also used for Furuno data types
SINT2 = {"fmt": "h", "dtype": "int16"}
SINT4 = {"fmt": "i", "dtype": "int32"}
UINT1 = {"fmt": "B", "dtype": "unit8"}
UINT2 = {"fmt": "H", "dtype": "uint16"}
UINT4 = {"fmt": "I", "dtype": "unint32"}



Loading

0 comments on commit dd7418c

Please sign in to comment.