Skip to content

API

Coordinates Conversion

Coordinate Transformations module.

This module provides vectorized coordinate transformations for terrestrial and celestial reference frames commonly used in SAR and orbital geometry computations. It leverages pyproj for geodetic conversions and Astropy for precise celestial frame transformations.

Coordinate Systems Supported

  • ECEF (EPSG:4978): Earth-Centered Earth-Fixed cartesian coordinates (X, Y, Z) in meters
  • LLH (EPSG:4326): Geodetic coordinates, Latitude, Longitude in radians/degrees, Height in meters
  • UTM (EPSG:326xx/327xx): Universal Transverse Mercator coordinates (Easting, Northing, Height) in meters
  • ECI (GCRS): Earth-Centered Inertial (Geocentric Celestial Reference System)

All functions support both single point (shape (3,)) and batch operations on arrays of coordinates (shape (N, 3)). The ECEF<->ECI transformations properly account for Earth rotation requiring a precise UTC timestamp via PreciseDateTime and velocities.

Attributes

LLH_CRS module-attribute

Python
LLH_CRS = CRS.from_epsg(4326)

ECEF_CRS module-attribute

Python
ECEF_CRS = CRS.from_epsg(4978)

UTM_EPSG_NORTH_BASE module-attribute

Python
UTM_EPSG_NORTH_BASE = 32600

UTM_EPSG_SOUTH_BASE module-attribute

Python
UTM_EPSG_SOUTH_BASE = 32700

xyz2llh_transformer module-attribute

Python
xyz2llh_transformer = Transformer.from_crs(ECEF_CRS, LLH_CRS)

llh2xyz_transformer module-attribute

Python
llh2xyz_transformer = Transformer.from_crs(LLH_CRS, ECEF_CRS)

__all__ module-attribute

Python
__all__ = ['ecef2eci', 'eci2ecef', 'llh2utm', 'llh2xyz', 'utm2llh', 'xyz2llh']

Classes

Functions:

xyz2llh

Python
xyz2llh(coordinates: NDArray[floating], *, radians: bool = True) -> npt.NDArray[np.floating]

Convert XYZ ECEF coordinates (epsg:4978) to LLH geodetic coordinates (epsg:4326).

Output latitude and longitude may be returned in [deg] if radians input flag is set to False.

Parameters:

Name Type Description Default
coordinates NDArray[floating]

XYZ EEF coordinates [m] (epsg:4978), with shape (3,) or (N, 3), with 3 being X, Y and Z in meters

required
radians bool

if output latitude and longitude must be expressed in radians, otherwise they are provided in deg, by default True

True

Returns:

Type Description
NDArray[floating]

LLH geodetic coordinates (epsg:4326), with shape (3,) or (N, 3), with 3 being Lat [rad/deg], Lon [rad/deg] and H [m]

llh2xyz

Python
llh2xyz(coordinates: NDArray[floating], *, radians: bool = True) -> npt.NDArray[np.floating]

Convert from LLH geodetic coordinates (epsg:4326) to XYZ ECEF coordinates (epsg:4978).

Input latitude and longitude may be provided in [deg] if radians input flag is set to False.

Parameters:

Name Type Description Default
coordinates NDArray[floating]

llh geodetic coordinates (epsg:4326), with shape (3,) or (N, 3), with 3 being Lat [rad/deg], Lon [rad/deg] and H [m]

required
radians bool

if input latitude and longitude are expressed in radians, otherwise they can be provided in deg, by default True

True

Returns:

Type Description
NDArray[floating]

XYZ EEF coordinates (epsg:4978), with shape (3,) or (N, 3), with 3 being X, Y and Z in meters

ecef2eci

Python
ecef2eci(positions: NDArray[floating], velocities: NDArray[floating], times: PreciseDateTime | NDArray) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]

Convert from XYZ ECEF (epsg:4978 ITRS) positions and velocities to XYZ ECI (GCRS) coordinates.

Parameters:

Name Type Description Default
positions NDArray[floating]

positions in ECEF coordinates expressed in [m], with shape (3,) or (N, 3)

required
velocities NDArray[floating]

velocities in ECEF coordinates expressed in [m/s], with shape (3,) or (N, 3)

required
times PreciseDateTime | NDArray

observation times [UTC] associated to the input positions and velocities, scalar or with shape (N,)

required

Returns:

Type Description
NDArray[floating]

XYZ ECI position coordinates expressed in [m], with shape (3,) or (N, 3)

NDArray[floating]

XYZ ECI velocities coordinates expressed in [m/s], with shape (3,) or (N, 3)

eci2ecef

Python
eci2ecef(positions: NDArray[floating], velocities: NDArray[floating], times: PreciseDateTime | NDArray) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]

Convert from XYZ ECI (GCRS) positions and velocities to XYZ ECEF (epsg:4978 ITRS) coordinates.

Parameters:

Name Type Description Default
positions NDArray[floating]

positions in ECI coordinates expressed in [m], with shape (3,) or (N, 3)

required
velocities NDArray[floating]

velocities in ECI coordinates expressed in [m/s], with shape (3,) or (N, 3)

required
times PreciseDateTime | NDArray

observation times [UTC] associated to the input positions and velocities, scalar or with shape (N,)

required

Returns:

Type Description
NDArray[floating]

XYZ ECEF position coordinates expressed in [m], with shape (3,) or (N, 3)

NDArray[floating]

XYZ ECEF velocities coordinates expressed in [m/s], with shape (3,) or (N, 3)

utm2llh

Python
utm2llh(coordinates: NDArray[floating], zone: str, *, radians: bool = True) -> npt.NDArray[np.floating]

Convert UTM (Easting, Northing, Height) coordinates (epsg:326xx/327xx) to LLH geodetic coordinates (epsg:4326).

Parameters:

Name Type Description Default
coordinates NDArray[floating]

UTM coordinates with shape (3,) or (N, 3), with 3 being Easting, Northing, and Height in meters

required
zone str

UTM zone in format 'ZZH' where ZZ is zone number (1-60) and H is hemisphere ('N' for northern, 'S' for southern) Example: '33N', '33S', '1N', '60S'

required
radians bool

if output latitude and longitude must be expressed in radians, otherwise they are provided in deg, by default True

True

Returns:

Type Description
NDArray[floating]

LLH geodetic coordinates (epsg:4326), with shape (3,) or (N, 3), with 3 being Lat [rad/deg], Lon [rad/deg] and H [m]

Raises:

Type Description
ValueError

if zone format is invalid or zone number is out of range (1-60)

llh2utm

Python
llh2utm(coordinates: NDArray[floating], zone: str, *, radians: bool = True) -> npt.NDArray[np.floating]

Convert LLH geodetic coordinates (epsg:4326) to UTM (Easting, Northing, Height) coordinates (epsg:326xx/327xx).

Parameters:

Name Type Description Default
coordinates NDArray[floating]

LLH geodetic coordinates (epsg:4326), with shape (3,) or (N, 3), with 3 being Lat [rad/deg], Lon [rad/deg] and H [m]

required
zone str

UTM zone in format 'ZZH' where ZZ is zone number (1-60) and H is hemisphere ('N' for northern, 'S' for southern) Example: '33N', '33S', '1N', '60S'

required
radians bool

if input latitude and longitude are expressed in radians, otherwise they can be provided in deg, by default True

True

Returns:

Type Description
NDArray[floating]

UTM coordinates, with shape (3,) or (N, 3), with 3 being Easting [m], Northing [m], and Height [m]

Raises:

Type Description
ValueError

if zone format is invalid or zone number is out of range (1-60)

Ellipsoid

Ellipsoid models and utilities for geodetic computations.

Attributes

WGS84 module-attribute

Python
WGS84 = Geod(ellps='WGS84')

__all__ module-attribute

Python
__all__ = ['WGS84', 'compute_line_ellipsoid_intersections', 'create_inflated_wgs84_ellipsoid']

Functions:

create_inflated_wgs84_ellipsoid

Python
create_inflated_wgs84_ellipsoid(height: float) -> Geod

Create an inflated WGS84 ellipsoid.

Parameters:

Name Type Description Default
height float

height above WGS84 ellipsoid

required

Returns:

Type Description
Geod

inflated WGS84-like ellipsoid

compute_line_ellipsoid_intersections

Python
compute_line_ellipsoid_intersections(line_directions: NDArray[floating], line_origins: NDArray[floating], ellipsoid: Geod) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]

Compute the intersections between lines and an ellipsoid.

For each line, returns two intersection arrays. When a line does not intersect the ellipsoid, both arrays contain NaN. When a line is tangent, both arrays contain the same point. The first array always contains the intersection closest to line_origin.

Parameters:

Name Type Description Default
line_directions NDArray[floating]

(3,), (N, 3) one or more line directions, not necessarily normalized

required
line_origins NDArray[floating]

(3,), (N, 3) one or more line origins

required
ellipsoid Geod

ellipsoid

required

Returns:

Type Description
NDArray[floating]

closest intersections to line_origins, with shape (3,) for a single line or (N, 3) for multiple lines, np.nan is used as a placeholder where no intersection exists

NDArray[floating]

farthest intersections to line_origins, with shape (3,) for a single line or (N, 3) for multiple lines, np.nan is used as a placeholder where no intersection exists

Raises:

Type Description
ValueError

In case of invalid input

Examples:

no intersection

Python Console Session
>>> first, second = compute_line_ellipsoid_intersections([0, 0, 100], [-5, 0, 2], Geod(a=2, b=1))
>>> print(first, second)
[nan nan nan] [nan nan nan]

tangent intersection (first == second)

Python Console Session
>>> first, second = compute_line_ellipsoid_intersections([100, 0, 0], [-5, 0, 1], Geod(a=2, b=1))
>>> print(first, second)
[0. 0. 1.] [0. 0. 1.]

two intersections, first one is closer to line_origin

Python Console Session
>>> first, second = compute_line_ellipsoid_intersections([100, 0, 0], [-5, 0, 0], Geod(a=2, b=1))
>>> print(first, second)
[-2.  0.  0.] [2. 0. 0.]

multiple lines

Python Console Session
>>> line_origins = np.array([[-5, 0, 2], [-5, 0, 1], [-5, 0, 0]])
>>> line_directions = np.array([[0, 0, 100], [100, 0, 0], [100, 0, 0]])
>>> first, second = compute_line_ellipsoid_intersections(line_directions, line_origins, Geod(a=2, b=1))
>>> print(first)
[[nan nan nan]
 [ 0.  0.  1.]
 [-2.  0.  0.]]
>>> print(second)
[[nan nan nan]
 [ 0.  0.  1.]
 [ 2.  0.  0.]]

extract validity mask for multiple lines

Python Console Session
>>> line_origins = np.array([[-5, 0, 2], [-5, 0, 1], [-5, 0, 0]])
>>> line_directions = np.array([[0, 0, 100], [100, 0, 0], [100, 0, 0]])
>>> first, second = compute_line_ellipsoid_intersections(line_directions, line_origins, Geod(a=2, b=1))
>>> valid_mask = ~np.isnan(first[:, 0])
>>> print(first[valid_mask], second[valid_mask])
[[ 0.  0.  1.]
 [-2.  0.  0.]] [[0. 0. 1.]
 [2. 0. 0.]]