"""
Functions for reading NEXRAD Level 3 products.
"""
import numpy as np
from ..config import FileMetadata, get_fillvalue
from ..core.radar import Radar
from .common import _test_arguments, make_time_unit_str, prepare_for_read
from .nexrad_level3 import NEXRADLevel3File
[docs]def read_nexrad_level3(
filename,
field_names=None,
additional_metadata=None,
file_field_names=False,
exclude_fields=None,
include_fields=None,
storage_options={"anon": True},
**kwargs
):
"""
Read a NEXRAD Level 3 product.
Parameters
----------
filename : str
Filename of NEXRAD Level 3 product file. The files hosted by
at the NOAA National Climate Data Center [1]_ as well as on the
NWS WSR-88D Level III Data Collection and Distribution Network
have been tests. Other NEXRAD Level 3 files may or may not work.
A file-like object pointing to the beginning of such a file is also
supported [2]_.
field_names : dict, optional
Dictionary mapping NEXRAD level 3 product number to radar field names.
If the product number of the file does not appear in this dictionary
or has a value of None it will not be placed in the radar.fields
dictionary. A value of None, the default, will use the mapping
defined in the metadata configuration file.
additional_metadata : dict of dicts, optional
Dictionary of dictionaries to retrieve metadata from during this read.
This metadata is not used during any successive file reads unless
explicitly included. A value of None, the default, will not
introduct any addition metadata and the file specific or default
metadata as specified by the metadata configuration file will be used.
file_field_names : bool, optional
True to use the product number for the field name. In this case the
field_names parameter is ignored. The field dictionary will likely
only have a 'data' key, unless the fields are defined in
`additional_metadata`.
exclude_fields : list or None, optional
List of fields to exclude from the radar object. This is applied
after the `file_field_names` and `field_names` parameters. Set
to None to include all fields specified by include_fields.
include_fields : list or None, optional
List of fields to include from the radar object. This is applied
after the `file_field_names` and `field_names` parameters. Set
to None to include all fields not specified by exclude_fields.
**kwargs
Additional keyword arguments to pass to fsspec to open the dataset
Returns
-------
radar : Radar
Radar object containing all moments and sweeps/cuts in the volume.
Gates not collected are masked in the field data.
References
----------
.. [1] http://www.ncdc.noaa.gov/
.. [2] http://www.roc.noaa.gov/wsr88d/Level_III/Level3Info.asp
"""
# test for non empty kwargs
_test_arguments(kwargs)
# create metadata retrieval object
filemetadata = FileMetadata(
"nexrad_level3",
field_names,
additional_metadata,
file_field_names,
exclude_fields,
include_fields,
)
# open the file
nfile = NEXRADLevel3File(
prepare_for_read(filename, storage_options=storage_options)
)
nradials = nfile.packet_header["nradials"]
msg_code = nfile.msg_header["code"]
# time
time = filemetadata("time")
time_start = nfile.get_volume_start_datetime()
time["units"] = make_time_unit_str(time_start)
time["data"] = np.zeros((nradials,), dtype="float64")
# range
_range = filemetadata("range")
_range["data"] = nfile.get_range()
_range["meters_to_center_of_first_gate"] = _range["data"][0]
_range["meters_between_gates"] = _range["data"][1] - _range["data"][0]
# fields
fields = {}
field_name = filemetadata.get_field_name(msg_code)
if field_name is None:
fields = {}
else:
dic = filemetadata(field_name)
dic["_FillValue"] = get_fillvalue()
dic["data"] = nfile.get_data()
fields = {field_name: dic}
# metadata
metadata = filemetadata("metadata")
metadata["original_container"] = "NEXRAD Level 3"
# scan_type
scan_type = "ppi"
# latitude, longitude, altitude
latitude = filemetadata("latitude")
longitude = filemetadata("longitude")
altitude = filemetadata("altitude")
lat, lon, height = nfile.get_location()
# Nexrad altitude is in feet, convert to meters unless user's
# default config has units in feet.
if altitude["units"] == "meters":
height = height * 0.3048
latitude["data"] = np.array([lat], dtype="float64")
longitude["data"] = np.array([lon], dtype="float64")
altitude["data"] = np.array([height], dtype="float64")
# sweep_number, sweep_mode, fixed_angle, sweep_start_ray_index
# sweep_end_ray_index
sweep_number = filemetadata("sweep_number")
sweep_mode = filemetadata("sweep_mode")
sweep_start_ray_index = filemetadata("sweep_start_ray_index")
sweep_end_ray_index = filemetadata("sweep_end_ray_index")
sweep_number["data"] = np.array([0], dtype="int32")
sweep_mode["data"] = np.array(1 * ["azimuth_surveillance"], dtype="S")
sweep_start_ray_index["data"] = np.array([0], dtype="int32")
sweep_end_ray_index["data"] = np.array([nradials - 1], dtype="int32")
# azimuth, elevation, fixed_angle
azimuth = filemetadata("azimuth")
elevation = filemetadata("elevation")
fixed_angle = filemetadata("fixed_angle")
azimuth["data"] = nfile.get_azimuth()
elev = nfile.get_elevation()
elevation["data"] = np.ones((nradials,), dtype="float32") * elev
fixed_angle["data"] = np.array([elev], dtype="float32")
nfile.close()
return Radar(
time,
_range,
fields,
metadata,
scan_type,
latitude,
longitude,
altitude,
sweep_number,
sweep_mode,
fixed_angle,
sweep_start_ray_index,
sweep_end_ray_index,
azimuth,
elevation,
instrument_parameters=None,
)