@@ -3425,10 +3425,36 @@ def test_no_geoloc_raises(self) -> None:
34253425 _find_geoloc_pair (ds )
34263426
34273427 def test_ambiguous_geoloc_raises (self ) -> None :
3428- """Multiple recognised pairs should raise with all pairs listed."""
3428+ """Multiple recognised pairs should raise with all pairs listed.
3429+
3430+ Uses a dataset where cf_xarray cannot resolve the ambiguity (no CF
3431+ attributes, and variable names are not recognised CF standard names).
3432+ """
3433+ from point_collocation .core .engine import _find_geoloc_pair
3434+
3435+ # Has both (lon, lat) and (Longitude, Latitude) — neither pair uses the
3436+ # exact CF standard name spellings that cf_xarray can resolve by name,
3437+ # so the name-based fallback detects two pairs and raises.
3438+ ds = xr .Dataset (
3439+ coords = {
3440+ "lon" : [0.0 ],
3441+ "lat" : [0.0 ],
3442+ "Longitude" : [0.0 ],
3443+ "Latitude" : [0.0 ],
3444+ }
3445+ )
3446+ with pytest .raises (ValueError , match = "ambiguous geolocation variables" ):
3447+ _find_geoloc_pair (ds )
3448+
3449+ def test_lon_lat_plus_longitude_latitude_resolves_via_cf (self ) -> None :
3450+ """When cf_xarray is installed, (lon,lat)+(longitude,latitude) resolves.
3451+
3452+ Without cf_xarray this would be ambiguous, but cf_xarray correctly
3453+ picks the CF standard-name pair ``(longitude, latitude)``.
3454+ """
3455+ cf_xarray = pytest .importorskip ("cf_xarray" ) # noqa: F841
34293456 from point_collocation .core .engine import _find_geoloc_pair
34303457
3431- # Has both (lon, lat) and (longitude, latitude)
34323458 ds = xr .Dataset (
34333459 coords = {
34343460 "lon" : [0.0 ],
@@ -3437,9 +3463,113 @@ def test_ambiguous_geoloc_raises(self) -> None:
34373463 "latitude" : [0.0 ],
34383464 }
34393465 )
3466+ lon_name , lat_name = _find_geoloc_pair (ds )
3467+ assert lon_name == "longitude"
3468+ assert lat_name == "latitude"
3469+
3470+
3471+ class TestGeolocDetectionCfXarray :
3472+ """Tests for _find_geoloc_pair() using cf_xarray CF-convention detection."""
3473+
3474+ def test_finds_via_standard_name (self ) -> None :
3475+ pytest .importorskip ("cf_xarray" )
3476+ from point_collocation .core .engine import _find_geoloc_pair
3477+
3478+ ds = xr .Dataset (
3479+ coords = {
3480+ "myX" : xr .DataArray (
3481+ [0.0 ], attrs = {"standard_name" : "longitude" , "units" : "degrees_east" }
3482+ ),
3483+ "myY" : xr .DataArray (
3484+ [0.0 ], attrs = {"standard_name" : "latitude" , "units" : "degrees_north" }
3485+ ),
3486+ }
3487+ )
3488+ lon_name , lat_name = _find_geoloc_pair (ds )
3489+ assert lon_name == "myX"
3490+ assert lat_name == "myY"
3491+
3492+ def test_finds_via_units (self ) -> None :
3493+ pytest .importorskip ("cf_xarray" )
3494+ from point_collocation .core .engine import _find_geoloc_pair
3495+
3496+ ds = xr .Dataset (
3497+ coords = {
3498+ "mylon" : xr .DataArray ([0.0 ], attrs = {"units" : "degrees_east" }),
3499+ "mylat" : xr .DataArray ([0.0 ], attrs = {"units" : "degrees_north" }),
3500+ }
3501+ )
3502+ lon_name , lat_name = _find_geoloc_pair (ds )
3503+ assert lon_name == "mylon"
3504+ assert lat_name == "mylat"
3505+
3506+ def test_finds_via_long_name (self ) -> None :
3507+ pytest .importorskip ("cf_xarray" )
3508+ from point_collocation .core .engine import _find_geoloc_pair
3509+
3510+ ds = xr .Dataset (
3511+ coords = {
3512+ "x" : xr .DataArray ([0.0 ], attrs = {"long_name" : "longitude" }),
3513+ "y" : xr .DataArray ([0.0 ], attrs = {"long_name" : "latitude" }),
3514+ }
3515+ )
3516+ lon_name , lat_name = _find_geoloc_pair (ds )
3517+ assert lon_name == "x"
3518+ assert lat_name == "y"
3519+
3520+ def test_finds_cf_attrs_in_data_vars (self ) -> None :
3521+ """CF-detected geoloc vars stored as data_vars (not coords) should be found."""
3522+ pytest .importorskip ("cf_xarray" )
3523+ from point_collocation .core .engine import _find_geoloc_pair
3524+
3525+ ds = xr .Dataset (
3526+ {
3527+ "nav_lon" : xr .DataArray (
3528+ [[0.0 , 1.0 ]],
3529+ dims = ["y" , "x" ],
3530+ attrs = {"standard_name" : "longitude" },
3531+ ),
3532+ "nav_lat" : xr .DataArray (
3533+ [[0.0 , 0.0 ]],
3534+ dims = ["y" , "x" ],
3535+ attrs = {"standard_name" : "latitude" },
3536+ ),
3537+ "sst" : xr .DataArray ([[25.0 , 26.0 ]], dims = ["y" , "x" ]),
3538+ }
3539+ )
3540+ lon_name , lat_name = _find_geoloc_pair (ds )
3541+ assert lon_name == "nav_lon"
3542+ assert lat_name == "nav_lat"
3543+
3544+ def test_cf_ambiguous_raises (self ) -> None :
3545+ """Multiple CF-detected longitude vars should raise ambiguous error."""
3546+ pytest .importorskip ("cf_xarray" )
3547+ from point_collocation .core .engine import _find_geoloc_pair
3548+
3549+ ds = xr .Dataset (
3550+ coords = {
3551+ "lon1" : xr .DataArray ([0.0 ], attrs = {"standard_name" : "longitude" }),
3552+ "lon2" : xr .DataArray ([0.0 ], attrs = {"standard_name" : "longitude" }),
3553+ "lat1" : xr .DataArray ([0.0 ], attrs = {"standard_name" : "latitude" }),
3554+ }
3555+ )
34403556 with pytest .raises (ValueError , match = "ambiguous geolocation variables" ):
34413557 _find_geoloc_pair (ds )
34423558
3559+ def test_cf_partial_detection_raises (self ) -> None :
3560+ """CF detects longitude but not latitude — should raise 'no geolocation'."""
3561+ pytest .importorskip ("cf_xarray" )
3562+ from point_collocation .core .engine import _find_geoloc_pair
3563+
3564+ ds = xr .Dataset (
3565+ coords = {
3566+ "myX" : xr .DataArray ([0.0 ], attrs = {"standard_name" : "longitude" }),
3567+ "temperature" : xr .DataArray ([20.0 ]),
3568+ }
3569+ )
3570+ with pytest .raises (ValueError , match = "no geolocation variables found" ):
3571+ _find_geoloc_pair (ds )
3572+
34433573
34443574class TestGeometryEnforcement :
34453575 """Tests for _check_geometry()."""
0 commit comments