# Coordinates Particle positions are in grid-cell units: cell `i` of a 0-based array covers `i <= x < i + 1`, with its centre at `x = i + 0.5`, and the same for `y` (see [Grid](../components/grid.md#index-conventions)). Saved positions are in the **full-grid** frame, whatever subset the run used. ## With an adapter `gcm.glorys`, `gcm.ecco2` and `gcm.scrippsgog` have the same three functions: ```python import numpy as np import pandas as pd from tracmass.gcm import glorys as model # or ecco2, scrippsgog # Geographic -> model positions, e.g. for seeding lon = np.array([-30.0, 10.0, 5.0]) lat = np.array([45.0, 55.0, 60.0]) x, y = model.lonlat_to_xy(lon, lat) # 0-based fractional, full grid # Model positions -> geographic, e.g. for analysis df = pd.read_parquet("trm_omi.parquet", columns=["x", "y"]) lon, lat = model.xy_to_lonlat(df["x"], df["y"]) # Cell-centre lon/lat of a run grid (follows the subset), each (imt, jmt) lon2d, lat2d = model.lonlat_grid(grid) ``` - `lonlat_to_xy` finds the nearest ocean-cell centre with a great-circle KD-tree, then refines the position inside the cell. It is the exact inverse of `xy_to_lonlat`. Points on land fall back to the centre of the nearest ocean cell. - `xy_to_lonlat` interpolates bilinearly between cell centres in Cartesian (unit-sphere) coordinates, which is correct on curvilinear grids and across the ±180° seam. - Both use full-grid indices. To seed on a subset grid, subtract `grid.i_offset` and `grid.j_offset`, or use `seed.latlon_list`, which does this for you. - `tracmass.load(..., lonlat=True)` calls `xy_to_lonlat` for you. ## For any grid — `tracmass.trajs` The generic functions take any object with `.lon` and `.lat` attributes (optionally `.kmt`), or a plain `(lon, lat[, kmt])` tuple: ```python import types from tracmass.trajs import xy_to_lonlat, lonlat_to_xy grid = types.SimpleNamespace(lon=lon_arr, lat=lat_arr, kmt=kmt_arr) lon, lat = xy_to_lonlat(df["x"], df["y"], grid) x, y = lonlat_to_xy(lon, lat, grid) # round-trips xy_to_lonlat exactly ``` - `lon` and `lat` can be **1-D** (`lon` of length `imt`, `lat` of length `jmt`, a regular grid) or **2-D** in either axis order. 2-D arrays that look like `(jmt, imt)`, as from `np.meshgrid(lon, lat)`, are transposed to `(imt, jmt)` with a warning. Pass `np.meshgrid(lon, lat, indexing="ij")` to avoid it. - Zonal periodicity (a global grid whose last column wraps onto the first) is detected automatically. Pass `periodic=True` or `periodic=False` to override it. - On non-periodic edges, positions in the outer half cell are extrapolated linearly from the two edge centres.