Assign GeoCoords#
This notebook demonstrates how to work with geocoordinates (longitude and latitude) instead of radar-centric x and y coordinates.
[1]:
import cmweather # noqa: F401
import matplotlib.pyplot as plt
import xarray as xr
from open_radar_data import DATASETS
import xradar as xd
Define Functions#
[2]:
def get_geocoords(ds):
"""
Converts Cartesian coordinates (x, y, z) in a radar dataset to geographic
coordinates (longitude, latitude, altitude) using CRS transformation.
Parameters
----------
ds : xarray.Dataset
Radar dataset with Cartesian coordinates.
Returns
-------
xarray.Dataset
Dataset with added 'lon', 'lat', and 'alt' coordinates and their attributes.
"""
from pyproj import CRS, Transformer
# Convert the dataset to georeferenced coordinates
ds = ds.xradar.georeference()
# Define source and target coordinate reference systems (CRS)
src_crs = ds.xradar.get_crs()
trg_crs = CRS.from_user_input(4326) # EPSG:4326 (WGS 84)
# Create a transformer for coordinate conversion
transformer = Transformer.from_crs(src_crs, trg_crs)
# Transform x, y, z coordinates to latitude, longitude, and altitude
trg_y, trg_x, trg_z = transformer.transform(ds.x, ds.y, ds.z)
# Assign new coordinates with appropriate attributes
ds = ds.assign_coords(
{
"lon": (ds.x.dims, trg_x, xd.model.get_longitude_attrs()),
"lat": (ds.y.dims, trg_y, xd.model.get_latitude_attrs()),
"alt": (ds.z.dims, trg_z, xd.model.get_altitude_attrs()),
}
)
return ds
def fix_sitecoords(ds):
coords = ["longitude", "latitude", "altitude", "altitude_agl"]
for coord in coords:
# Compute median excluding NaN
data = ds[coord].median(skipna=True).item()
attrs = ds[coord].attrs if coord in ds else {}
ds = ds.assign_coords({coord: xr.DataArray(data=data, attrs=attrs)})
return ds
Load Data#
[3]:
file1 = DATASETS.fetch("cfrad.20080604_002217_000_SPOL_v36_SUR.nc")
file2 = DATASETS.fetch("cfrad.20211011_201557.188_to_20211011_201617.720_DOW8_PPI.nc")
Downloading file 'cfrad.20080604_002217_000_SPOL_v36_SUR.nc' from 'https://github.com/openradar/open-radar-data/raw/main/data/cfrad.20080604_002217_000_SPOL_v36_SUR.nc' to '/home/docs/.cache/open-radar-data'.
Downloading file 'cfrad.20211011_201557.188_to_20211011_201617.720_DOW8_PPI.nc' from 'https://github.com/openradar/open-radar-data/raw/main/data/cfrad.20211011_201557.188_to_20211011_201617.720_DOW8_PPI.nc' to '/home/docs/.cache/open-radar-data'.
Example #1#
[4]:
dtree1 = xd.io.open_cfradial1_datatree(file1)
[5]:
dtree1 = dtree1.xradar.georeference()
[6]:
display(dtree1["sweep_0"].ds)
<xarray.DatasetView> Size: 15MB Dimensions: (azimuth: 483, range: 996) Coordinates: time (azimuth) datetime64[ns] 4kB 2008-06-04T00:15:... * range (range) float32 4kB 150.0 300.0 ... 1.494e+05 * azimuth (azimuth) float32 2kB 0.0 0.75 ... 358.5 359.2 elevation (azimuth) float32 2kB 0.5164 0.5219 ... 0.5219 latitude float64 8B 22.53 longitude float64 8B 120.4 altitude float64 8B 45.0 crs_wkt int64 8B 0 x (azimuth, range) float64 4MB 0.0 ... -1.955e+03 y (azimuth, range) float64 4MB 150.0 ... 1.493e+05 z (azimuth, range) float64 4MB 46.35 ... 2.718e+03 Data variables: (12/18) sweep_number int32 4B ... sweep_mode <U20 80B 'azimuth_surveillance' prt_mode |S32 32B ... follow_mode |S32 32B ... sweep_fixed_angle float32 4B ... pulse_width (azimuth) timedelta64[ns] 4kB ... ... ... r_calib_index (azimuth) int8 483B ... measured_transmit_power_h (azimuth) float32 2kB ... measured_transmit_power_v (azimuth) float32 2kB ... scan_rate (azimuth) float32 2kB ... DBZ (azimuth, range) float32 2MB ... VR (azimuth, range) float32 2MB ...
Assign lat, lon, and alt#
[7]:
dtree1 = dtree1.xradar.map_over_sweeps(get_geocoords)
[8]:
display(dtree1["sweep_0"].ds)
<xarray.DatasetView> Size: 27MB Dimensions: (azimuth: 483, range: 996) Coordinates: (12/14) time (azimuth) datetime64[ns] 4kB 2008-06-04T00:15:... * range (range) float32 4kB 150.0 300.0 ... 1.494e+05 * azimuth (azimuth) float32 2kB 0.0 0.75 ... 358.5 359.2 elevation (azimuth) float32 2kB 0.5164 0.5219 ... 0.5219 latitude float64 8B 22.53 longitude float64 8B 120.4 ... ... x (azimuth, range) float64 4MB 0.0 ... -1.955e+03 y (azimuth, range) float64 4MB 150.0 ... 1.493e+05 z (azimuth, range) float64 4MB 46.35 ... 2.718e+03 lon (azimuth, range) float64 4MB 120.4 ... 120.4 lat (azimuth, range) float64 4MB 22.53 ... 23.88 alt (azimuth, range) float64 4MB 46.35 ... 2.718e+03 Data variables: (12/18) sweep_number int32 4B ... sweep_mode <U20 80B 'azimuth_surveillance' prt_mode |S32 32B ... follow_mode |S32 32B ... sweep_fixed_angle float32 4B ... pulse_width (azimuth) timedelta64[ns] 4kB ... ... ... r_calib_index (azimuth) int8 483B ... measured_transmit_power_h (azimuth) float32 2kB ... measured_transmit_power_v (azimuth) float32 2kB ... scan_rate (azimuth) float32 2kB ... DBZ (azimuth, range) float32 2MB ... VR (azimuth, range) float32 2MB ...
[9]:
ds = dtree1["sweep_0"].to_dataset()
[10]:
fig, ax = plt.subplots(1, 2, figsize=(11, 4))
ds["DBZ"].plot(x="x", y="y", vmin=-10, vmax=75, cmap="HomeyerRainbow", ax=ax[0])
ds["DBZ"].plot(x="lon", y="lat", vmin=-10, vmax=75, cmap="HomeyerRainbow", ax=ax[1])
plt.show()
Example #2#
[11]:
dtree2 = xd.io.open_cfradial1_datatree(file2)
[12]:
try:
dtree2 = dtree2.xradar.georeference()
except Exception:
print("Georeferencing failed!")
Georeferencing failed!
[13]:
print("Longitude", dtree2["sweep_0"]["longitude"].shape)
print("Latitude", dtree2["sweep_0"]["latitude"].shape)
Longitude (731,)
Latitude (731,)
Important Note:
This radar data is from a mobile research radar called Doppler on Wheels (DOW), and its site coordinates (latitude, longitude) often vary slightly during operation, as can be seen from the shape ((731)) of the data in the above cell, while we expect it to be of unity shapes or empty, i.e., (1) or (). As a result, multiple site coordinate values can exist, creating a challenge for assigning consistent x, y, z or lat, lon, and alt coordinates using the current georeferencing system in xradar. To address this, a custom function like fix_sitecoords (defined above) can be created, leveraging the map_over_sweeps function to standardize the site coordinates.
Fix Coords#
[14]:
dtree2 = dtree2.xradar.map_over_sweeps(fix_sitecoords)
[15]:
dtree2 = dtree2.xradar.map_over_sweeps(get_geocoords)
[16]:
display(dtree2["sweep_0"].ds)
<xarray.DatasetView> Size: 116MB Dimensions: (azimuth: 731, range: 1984, frequency: 1) Coordinates: (12/16) * frequency (frequency) float32 4B 9.45e+09 altitude_agl float64 8B nan time (azimuth) datetime64[ns] 6kB 2021-10-11T20:15:... * range (range) float32 8kB 37.47 112.4 ... 1.487e+05 * azimuth (azimuth) float32 3kB 0.0 0.5 1.0 ... 359.0 359.5 elevation (azimuth) float32 3kB 0.4395 0.4395 ... 0.4395 ... ... x (azimuth, range) float64 12MB 0.0 ... -1.297e+03 y (azimuth, range) float64 12MB 37.47 ... 1.486e+05 z (azimuth, range) float64 12MB 211.3 ... 2.652e+03 lon (azimuth, range) float64 12MB -88.33 ... -88.35 lat (azimuth, range) float64 12MB 40.02 ... 41.35 alt (azimuth, range) float64 12MB 211.3 ... 2.652e+03 Data variables: (12/30) sweep_number float64 8B ... sweep_mode <U6 24B 'sector' prt_mode |S32 32B ... follow_mode |S32 32B ... sweep_fixed_angle float32 4B ... ray_start_range (azimuth) float32 3kB ... ... ... DBMHC (azimuth, range) float32 6MB ... DBZHC (azimuth, range) float32 6MB ... VEL (azimuth, range) float32 6MB ... VS1 (azimuth, range) float32 6MB ... VL1 (azimuth, range) float32 6MB ... WIDTH (azimuth, range) float32 6MB ...
[17]:
ds = dtree2["sweep_0"].to_dataset()
ref = ds.where(ds.DBZHC >= 5)["DBZHC"]
fig = plt.figure(figsize=(6, 5))
ax = plt.axes()
pl = ref.plot(
x="lon",
y="lat",
vmin=-10,
vmax=70,
cmap="HomeyerRainbow",
ax=ax,
)
ax.minorticks_on()
ax.grid(ls="--", lw=0.5)
ax.set_aspect("auto")
title = (
dtree2.attrs["instrument_name"]
+ " "
+ str(ds.time.mean().dt.strftime("%Y-%m-%d %H:%M:%S").values)
)
ax.set_title(title)
plt.show()
/home/docs/checkouts/readthedocs.org/user_builds/xradar/conda/latest/lib/python3.12/site-packages/numpy/_core/fromnumeric.py:57: RuntimeWarning: overflow encountered in multiply
return bound(*args, **kwds)
[ ]: