Extract a value from a curvilinear grid

A small notebook showing how to lookup the ij index from a given longitude and latitude in a curvilinear grid

import xarray as xr
import numpy as np
ds = xr.open_dataset('./Data/C10M-PNG-560/OCE/Analyse/SE/C10M-PNG-560_SE_4655_4704_1M_grid_T.nc')
ds
<xarray.Dataset>
Dimensions:               (axis_nbounds: 2, deptht: 31, tbnds: 2, time_counter: 12, x: 182, y: 149)
Coordinates:
  * time_counter          (time_counter) object 0000-01-16 00:00:00 ... 0000-...
    nav_lat               (y, x) float32 -78.19 -78.19 -78.19 ... 59.91 59.91
    nav_lon               (y, x) float32 105.0 107.0 109.0 ... 106.0 106.0 106.0
  * deptht                (deptht) float32 5.0 15.0 25.0 ... 4.75e+03 5.25e+03
    time_centered         (time_counter) object 4679-07-16 00:00:00 ... 4680-...
Dimensions without coordinates: axis_nbounds, tbnds, x, y
Data variables: (12/64)
    time_counter_bnds     (time_counter, tbnds) object 0000-01-01 00:00:00 .....
    deptht_bounds         (deptht, axis_nbounds) float32 0.0 10.0 ... 5.501e+03
    time_centered_bounds  (time_counter, axis_nbounds) object 4679-07-01 00:0...
    time_counter_bounds   (time_counter, axis_nbounds) float64 8.645e+10 ... ...
    e3t                   (time_counter, deptht, y, x) float32 ...
    pbo                   (time_counter, y, x) float32 ...
    ...                    ...
    rain                  (time_counter, y, x) float32 ...
    snow_ao_cea           (time_counter, y, x) float32 ...
    snow_ai_cea           (time_counter, y, x) float32 ...
    evap_ao_cea           (time_counter, y, x) float32 ...
    subl_ai_cea           (time_counter, y, x) float32 ...
    sosflxdo              (time_counter, y, x) float32 ...
Attributes:
    name:                      C10M-PNG-560_1m_grid_T
    description:               Created by xios
    title:                     Created by xios
    Conventions:               CF-1.6
    timeStamp:                 2019-Dec-10 12:48:46 GMT
    uuid:                      0b59c577-6754-47de-a26a-a13d8a514539
    LongName:                  IPSL 10 Ma,2X, BC from PlioMIP except for Aust...
    history:                   Wed Dec 11 05:53:06 2019: ncrcat -C --buffer_s...
    NCO:                       "4.6.0"
    nco_openmp_thread_number:  1

Small function to find the closest ij coordinates in the curvilinear longitudes and latitudes and returns the ij coordinates.

It would be good to be able to do this with multiple longitude and latitude values but the np.substract doesn't seem to be working correctly.
def find_nearest(lons, lats, lon0,lat0):
    idx = ((lons - lon0)**2+(lats - lat0)**2).argmin()
    return np.unravel_index(idx, lons.shape)

Get the ij coordinates

lon = 12
lat = 10
find_nearest(ds.nav_lon.values, ds.nav_lat.values, lon, lat)
(90, 133)

The find_nearest returns the indexs of the closest point so we use them with isel to get the value of the given variable (here tos)

y, x = find_nearest(ds.nav_lon.values, ds.nav_lat.values, lon, lat)
ds['tos'].isel(x=x, y=y)
<xarray.DataArray 'tos' (time_counter: 12)>
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan],
      dtype=float32)
Coordinates:
  * time_counter   (time_counter) object 0000-01-16 00:00:00 ... 0000-12-16 0...
    nav_lat        float32 10.51
    nav_lon        float32 11.0
    time_centered  (time_counter) object 4679-07-16 00:00:00 ... 4680-06-16 0...
Attributes:
    standard_name:       sea_surface_temperature
    long_name:           sea_surface_temperature
    units:               degC
    online_operation:    average
    interval_operation:  4800 s
    interval_write:      1 month
    cell_methods:        time: mean (interval: 4800 s) time_counter: mean
# Form [lon, lat]
coords = [
    [-170, 80],
    [-100, -80],
]

values = []
for coord in coords:
    lon, lat = coord
    y, x = find_nearest(ds.nav_lon.values, ds.nav_lat.values, lon, lat)
    value = float(ds["tos"].isel(x=x, y=y).mean("time_counter"))
    values.append(value)
    print(f'lon: {lon}\tlat: {lat}\t value: {value}')
lon: -170	lat: 80	 value: 0.07544342428445816
lon: -100	lat: -80	 value: nan
values
[0.07544342428445816, nan]