Skip to content

Commit

Permalink
Small bugfixes (#60)
Browse files Browse the repository at this point in the history
* Fix CF attrs for projected coordinates

* Fix range selection for custim dimensions

* lint

* More test cases
  • Loading branch information
mpiannucci authored Nov 18, 2024
1 parent 04affad commit 221db07
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 12 deletions.
4 changes: 2 additions & 2 deletions tests/test_cf_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ def test_cf_position_query(cf_client, cf_air_dataset, cf_temp_dataset):
axes = data["domain"]["axes"]

npt.assert_array_almost_equal(
axes["longitude"]["values"],
axes["x"]["values"],
[[x]],
), "Did not select nearby x coordinate"
npt.assert_array_almost_equal(
axes["latitude"]["values"],
axes["y"]["values"],
[[y]],
), "Did not select a nearby y coordinate"

Expand Down
37 changes: 35 additions & 2 deletions tests/test_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,39 @@ def test_select_query(regular_xy_dataset):
"Time is incorrect",
)

custom_dim_ds = xr.Dataset(
coords={
"lat": np.arange(45, 47),
"lon": np.arange(200, 202),
"elevation": np.arange(100, 105),
"step": pd.timedelta_range("0 days", periods=72, freq="1H"),
},
data_vars={
"air": (("lat", "lon", "elevation", "step"), np.random.rand(2, 2, 5, 72)),
},
)

query = EDRQuery(
coords="POINT(201 46)",
parameters="air",
method="linear",
)
ds = query.select(custom_dim_ds, {"step": "0 hours/10 hours", "elevation": "101"})
assert ds["air"].shape == (2, 2, 1, 11), "Dataset shape is incorrect"
npt.assert_array_equal(
ds["step"],
pd.timedelta_range("0 days", periods=11, freq="1H"),
)
npt.assert_equal(ds["elevation"].values, 101)

ds = query.select(custom_dim_ds, {"step": "1 hours", "elevation": "101/103"})
assert ds["air"].shape == (2, 2, 3, 1), "Dataset shape is incorrect"
npt.assert_array_equal(
ds["step"],
pd.timedelta_range("1 hours", periods=1, freq="1H"),
)
npt.assert_equal(ds["elevation"].values, np.array([101, 102, 103]))


def test_select_query_error(regular_xy_dataset):
query = EDRQuery(
Expand Down Expand Up @@ -162,11 +195,11 @@ def test_select_position_projected_xy(projected_xy_dataset):

projected_ds = project_dataset(ds, query.crs)
(
npt.assert_approx_equal(projected_ds.longitude.values, 64.59063409),
npt.assert_approx_equal(projected_ds.cf["X"].values, 64.59063409),
"Longitude is incorrect",
)
(
npt.assert_approx_equal(projected_ds.latitude.values, 66.66454929),
npt.assert_approx_equal(projected_ds.cf["Y"].values, 66.66454929),
"Latitude is incorrect",
)
(
Expand Down
6 changes: 4 additions & 2 deletions xpublish_edr/geometry/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,12 @@ def project_dataset(ds: xr.Dataset, query_crs: str) -> xr.Dataset:
ds = ds.drop_vars(coords_to_drop)

# Create the new dataset with vectorized coordinates
x_var = xr.Variable(dims=target_dims, data=x, attrs=target_x_coord)
y_var = xr.Variable(dims=target_dims, data=y, attrs=target_y_coord)
ds = ds.assign_coords(
{
target_x_coord_name: (target_dims, x),
target_y_coord_name: (target_dims, y),
target_x_coord_name: x_var,
target_y_coord_name: y_var,
},
)

Expand Down
19 changes: 13 additions & 6 deletions xpublish_edr/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,32 @@ def select(self, ds: xr.Dataset, query_params: dict) -> xr.Dataset:
if query_param in edr_query_params:
del query_params[query_param]

sel_params = {}
sliced_sel_params = {}
for key, value in query_params.items():
split_value = value.split("/")
split_value = [float(v) if v.isnumeric() else v for v in value.split("/")]
if len(split_value) == 1:
query_params[key] = [split_value[0]]
sel_params[key] = [split_value[0]]
elif len(split_value) == 2:
query_params[key] = slice(split_value[0], split_value[1])
sliced_sel_params[key] = slice(split_value[0], split_value[1])
else:
raise ValueError(f"Too many values for selecting {key}")

# We separate the slice selection from the single value selection in order to take
# advantage of selection method which breaks when mixing the two
if len(sliced_sel_params) > 0:
ds = ds.sel(sliced_sel_params)

if self.method == "nearest":
ds = ds.sel(query_params, method=self.method)
ds = ds.sel(sel_params, method=self.method)
else:
# Interpolation may not be supported for all possible selection
# parameters, so we provide a fallback to xarray's nearest selection
try:
ds = ds.interp(query_params, method=self.method)
ds = ds.interp(sel_params, method=self.method)
except Exception as e:
logger.warning(f"Interpolation failed: {e}, falling back to selection")
ds = ds.sel(query_params, method="nearest")
ds = ds.sel(sel_params, method="nearest")

return ds

Expand Down

0 comments on commit 221db07

Please sign in to comment.