Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADD: Add get_gate_lat_lon_alt to pyart/xradar object #1716

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions pyart/xradar/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,10 @@ def __init__(self, xradar, default_sweep="sweep_0", scan_type=None):
self.metadata = dict(**self.xradar.attrs)
self.ngates = len(self.range["data"])
self.nrays = len(self.azimuth["data"])
self.projection = {"proj": "pyart_aeqd", "_include_lon_0_lat_0": True}
self.instrument_parameters = self.find_instrument_parameters()
self.init_gate_x_y_z()
self.init_gate_longitude_latitude()
self.init_gate_alt()

def __repr__(self):
Expand Down Expand Up @@ -582,6 +584,62 @@ def get_gate_x_y_z(self, sweep, edges=False, filter_transitions=False):
data = self.combined_sweeps.sel(sweep_number=sweep)
return data["x"].values, data["y"].values, data["z"].values

def get_gate_lat_lon_alt(
self, sweep, reset_gate_coords=False, filter_transitions=False
):
"""
Return the longitude, latitude and altitude gate locations.
Longitude and latitude are in degrees and altitude in meters.

With the default parameter this method returns the same data as
contained in the gate_latitude, gate_longitude and gate_altitude
attributes but this method performs the gate location calculations
only for the specified sweep and therefore is more efficient than
accessing this data through these attribute. If coordinates have
at all, please use the reset_gate_coords parameter.

Parameters
----------
sweep : int
Sweep number to retrieve gate locations from, 0 based.
reset_gate_coords : bool, optional
Optional to reset the gate latitude, gate longitude and gate
altitude attributes before using them in this function. This
is useful when the geographic coordinates have changed and gate
latitude, gate longitude and gate altitude need to be reset.
filter_transitions : bool, optional
True to remove rays where the antenna was in transition between
sweeps. False will include these rays. No rays will be removed
if the antenna_transition attribute is not available (set to None).

Returns
-------
lat, lon, alt : 2D array
Array containing the latitude, longitude and altitude,
for all gates in the sweep.

"""
s = self.get_slice(sweep)

if reset_gate_coords:
gate_latitude = LazyLoadDict(get_metadata("gate_latitude"))
gate_latitude.set_lazy("data", _gate_lon_lat_data_factory(self, 1))
self.gate_latitude = gate_latitude

gate_longitude = LazyLoadDict(get_metadata("gate_longitude"))
gate_longitude.set_lazy("data", _gate_lon_lat_data_factory(self, 0))
self.gate_longitude = gate_longitude

gate_altitude = LazyLoadDict(get_metadata("gate_altitude"))
gate_altitude.set_lazy("data", _gate_altitude_data_factory(self))
self.gate_altitude = gate_altitude

lat = self.gate_latitude["data"][s]
lon = self.gate_longitude["data"][s]
alt = self.gate_altitude["data"][s]

return lat, lon, alt

def init_gate_x_y_z(self):
"""Initialize or reset the gate_{x, y, z} attributes."""

Expand Down Expand Up @@ -616,6 +674,18 @@ def init_gate_alt(self):
data=np.mean(self.altitude["data"]) + self.gate_z["data"]
)

def init_gate_longitude_latitude(self):
"""
Initialize or reset the gate_longitude and gate_latitude attributes.
"""
gate_longitude = LazyLoadDict(get_metadata("gate_longitude"))
gate_longitude.set_lazy("data", _gate_lon_lat_data_factory(self, 0))
self.gate_longitude = gate_longitude

gate_latitude = LazyLoadDict(get_metadata("gate_latitude"))
gate_latitude.set_lazy("data", _gate_lon_lat_data_factory(self, 1))
self.gate_latitude = gate_latitude

def _combine_sweeps(self):
# Loop through and extract the different datasets
ds_list = []
Expand Down Expand Up @@ -789,6 +859,13 @@ def get_azimuth(self, sweep, copy=False):
else:
return azimuths

def get_projparams(self):
projparams = self.projection.copy()
if projparams.pop("_include_lon_0_lat_0", False):
projparams["lon_0"] = self.longitude["data"][0]
projparams["lat_0"] = self.latitude["data"][0]
return projparams


def _point_data_factory(grid, coordinate):
"""Return a function which returns the locations of all points."""
Expand Down Expand Up @@ -841,6 +918,41 @@ def _point_altitude_data():
return _point_altitude_data


def _gate_lon_lat_data_factory(radar, coordinate):
"""Return a function which returns the geographic locations of gates."""

def _gate_lon_lat_data():
"""The function which returns the geographic locations gates."""
x = radar.gate_x["data"]
y = radar.gate_y["data"]
projparams = radar.get_projparams()
geographic_coords = cartesian_to_geographic(x, y, projparams)
# Set gate_latitude['data'] when gate_longitude['data'] is evaluated
# and vice-versa. This ensures that both attributes contain data from
# the same map projection and that the map projection only needs to be
# evaluated once.
if coordinate == 0:
radar.gate_latitude["data"] = geographic_coords[1]
else:
radar.gate_longitude["data"] = geographic_coords[0]
return geographic_coords[coordinate]

return _gate_lon_lat_data


def _gate_altitude_data_factory(radar):
"""Return a function which returns the gate altitudes."""

def _gate_altitude_data():
"""The function which returns the gate altitudes."""
try:
return radar.altitude["data"] + radar.gate_z["data"]
except ValueError:
return np.mean(radar.altitude["data"]) + radar.gate_z["data"]

return _gate_altitude_data


@register_datatree_accessor("pyart")
class XradarDataTreeAccessor(XradarAccessor):
"""Adds a number of pyart specific methods to datatree.DataTree objects."""
Expand Down
16 changes: 16 additions & 0 deletions tests/xradar/test_accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ def test_get_gate_x_y_z(filename=filename):
assert z.shape == (480, 996)


def test_get_gate_lat_lon(filename=filename):
dtree = xd.io.open_cfradial1_datatree(
filename,
optional=False,
)
radar = pyart.xradar.Xradar(dtree)
lat, lon, alt = radar.get_gate_lat_lon_alt(0)
# Check lat, lon, and alt values
assert lat.shape == (480, 996)
assert_allclose(lat.min(), 21.183521)
assert lon.shape == (480, 996)
assert_allclose(lon.min(), 118.97935)
assert alt.shape == (480, 996)
assert_allclose(alt.min(), 45.0000017)


def test_add_field(filename=filename):
dtree = xd.io.open_cfradial1_datatree(
filename,
Expand Down
Loading