Skip to content

Commit

Permalink
ADD: Add get_gate_lat_lon_alt (#1716)
Browse files Browse the repository at this point in the history
  • Loading branch information
mgrover1 authored Jan 6, 2025
1 parent 3e84b69 commit d3796a6
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 0 deletions.
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

0 comments on commit d3796a6

Please sign in to comment.