astrix.spatial.location module

class Location[source]

Bases: Generic[T], ABC

Abstract base class for location objects (Point, Path). ‘interp’ function is required for integration with other modules.

abstractmethod convert_to(backend)[source]
Return type:

Location[TypeVar(T, bound= TimeLike, covariant=True)]

Parameters:

backend (str | Any | None)

property backend: str
property ecef: Any
property geodet: Any
abstract property is_singular: bool
property time: T
class Path(point, backend=None)[source]

Bases: Location[Time]

Path of multiple Point objects with associated Time. Enables interpolation between points over time and calculation of velocity. Must have at least 2 points with associated Time.

Parameters:
  • point (Point | list of Point) – Point object or list of Point objects with associated Time. If a list is provided, all Points must have the same backend and associated Time.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Examples

Instantiating a Path from a list of Points:

>>> from astrix.primitives import Point, Time, Path
>>> from datetime import datetime, timezone
>>> times = Time.from_datetime(
...     [
...         datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...         datetime(2025, 1, 1, 12, 0, 1, tzinfo=timezone.utc),
...         datetime(2025, 1, 1, 12, 0, 2, tzinfo=timezone.utc),
...     ]
... )
>>> path = Path(
...     [
...         Point([1, 2, 0], time=times[0]),
...         Point([2, 3.8, 0.4], time=times[1]),
...         Point([3, 6.0, 1], time=times[2]),
...     ]
... )  # Somewhere very hot in the middle of the Earth

Interpolate the Path to a new time and get velocity:

>>> path.interp(
        Time.from_datetime(datetime(2025, 1, 1, 12, 0, 1, 500000, tzinfo=timezone.utc)),
        method="linear"
    ).ecef # Interpolate to halfway between second and third point, return ECEF array
array([[2.5, 4.9, 0.7]])
>>> vel = path.interp_vel(
        Time.from_datetime(datetime(2025, 1, 1, 12, 0, 1, 500000, tzinfo=timezone.utc)),
    )
>>> vel.magnitude  # Interpolated velocity magnitude in m/s
array([2.48997992])
>>> vel.unit  # Interpolated unit velocity vector
array([[0.40160966, 0.88354126, 0.2409658 ]])
convert_to(backend)[source]

Convert the Path object to a different backend.

Return type:

Path

Parameters:

backend (str | Any | None)

downsample(dt_max)[source]

Downsample the Path to a maximum time step of dt_max seconds. Note: This function is not JIT-compatible due to data validation checks.

Return type:

Path

Parameters:

dt_max (float)

interp(time, method='linear', check_bounds=True)[source]

Interpolate the Path to the given times using the specified method.

Parameters:
  • time (Time) – Times to interpolate to.

  • method (str, optional) – Interpolation method. Currently only ‘linear’ is supported. Defaults to ‘linear’.

  • check_bounds (bool, optional) – Whether to check if the interpolation times are within the path time bounds. Defaults to True.

Returns:

Interpolated Point object at the given times.

Return type:

Point

interp_vel(time, method='linear', check_bounds=True)[source]

Interpolate the Path velocity to the given times using the specified method. Currently only ‘linear’ interpolation is supported.

Return type:

Velocity

Parameters:
  • time (Time)

  • method (str)

  • check_bounds (bool)

time_at_alt(alt)[source]

Find the times when the Path crosses the given altitude (in metres). Uses linear interpolation between points to find the crossing times. Note: This function is not JIT-compatible due to data validation checks.

Return type:

Time

Parameters:

alt (float)

truncate(start_time=None, end_time=None)[source]

Truncate the Path to the given start and end times. If start_time or end_time is None, the Path is truncated to the start or end of the Path respectively.

Note: This function is not JIT-compatible due to data validation checks.

Return type:

Path

Parameters:
  • start_time (Time | None)

  • end_time (Time | None)

property end_time: Time

Get the end time of the Path.

property is_singular: bool

Check if the Path object represents a single point. Always False for Path objects.

property points: Point

Get the list of Point objects that make up the Path.

property start_time: Time

Get the start time of the Path.

property vel: Velocity

Get the Velocity object associated with the Path.

class Point(ecef, time=TimeInvariant object, backend=None)[source]

Bases: Location[TimeLike]

Point(s) in ECEF coordinates, stored as (x, y, z) in metres. Can represent a single point or multiple points, and can be associated with a Time object for time instances of the points.

Parameters:
  • ecef (Array) – ECEF coordinates as (x, y, z) in metres. Shape (3,) or (1,3) for single points, (n, 3) for multiple points.

  • time (TimeLike, optional) – Time object associated with the points. If provided, the length of time must match the number of points. Defaults to TIME_INVARIANT for static points.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Examples

Single static point:

>>> from astrix.primitives import Point
>>> p1 = Point(
...     [-5047162.4, 2568329.79, -2924521.17]
... )  # ECEF coordinates of Brisbane in metres
>>> p1.geodet  # Convert to geodetic coordinates (lat, lon, alt)
array([[-27.47, 153.03, 0.0]])
>>> p2 = Point.from_geodet([-27.47, 153.03, 0])  # lat, lon in degrees, alt in metres
>>> p2.ecef  # Convert back to ECEF coordinates
array([[-5047162.4, 2568329.79, -2924521.17]])

Multiple static points:

>>> pts = Point(
...     [
...         [-5047162.4, 2568329.79, -2924521.17],  # Brisbane
...         [-2694045.0, -4293642.0, 3857878.0],  # San Francisco
...         [3877000.0, 350000.0, 5027000.0],  # Somewhere else
...     ]
... )
>>> pt_bris = pts[0]  # First point (Brisbane)
>>> assert len(pts) == 3

Dynamic point with time:

>>> from datetime import datetime, timezone
>>> from astrix.primitives import Time
>>> times = Time.from_datetime(
...     [
...         datetime(2021, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...         datetime(2021, 1, 1, 13, 0, 0, tzinfo=timezone.utc),
...         datetime(2021, 1, 1, 14, 0, 0, tzinfo=timezone.utc),
...     ]
... )
>>> pts_time = Point(
...     [
...         [-5047162.4, 2568329.79, -2924521.17],  # Brisbane
...         [-2694045.0, -4293642.0, 3857878.0],  # San Francisco
...         [3877000.0, 350000.0, 5027000.0],  # Somewhere else
...     ],
...     time=times,
... )
>>> pts.has_time
True
>>> pts.is_singular
False
>>> pts_new = pts + Point(
...     [[-1000, -1000, -1000]],
...     time=Time.from_datetime(
...         datetime(2021, 1, 1, 15, 0, 0, tzinfo=timezone.utc)
...     ),
... )
>>> assert len(pts_new) == 4

Notes

  • When associating a Time object, the length of the Time must match the number of points.

  • Use Path objects for interpolating between multiple points over time.

classmethod from_geodet(geodet, time=TimeInvariant object, backend=None)[source]

Create a Point object from geodetic coordinates (lat, lon, alt). Lat and lon are in degrees, alt is in meters.

Return type:

Point

Parameters:
  • geodet (Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str])

  • time (TimeLike)

  • backend (str | Any | None)

classmethod from_list(points)[source]

Create a Point object from a list of Point objects.

Return type:

Point

Parameters:

points (list[Point])

convert_to(backend)[source]

Convert the Point object to a different backend.

Return type:

Point

Parameters:

backend (str | Any | None)

property has_time: bool

Check if the Point has associated Time.

property is_singular: bool

Check if the Point object represents a single point.

class Velocity(vec, time, _xp)[source]

Bases: object

Velocity vector(s) in ECEF coordinates (vx, vy, vz) in m/s. Associated with a Time object for the time instances of the velocities. Internal use only, typically created from Path objects. No data validation is performed.

Parameters:
  • vec (Array) – Velocity vectors in ECEF coordinates (vx, vy, vz) in m/s. Shape (n, 3).

  • time (Time) – Time object associated with the velocities. Length must match number of velocity vectors.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

  • _xp (Any)

Examples

Velocity objects are typically created from Path objects.

>>> from astrix.primitives import Point, Time, Path
>>> from datetime import datetime, timezone
>>> times = Time.from_datetime(
...     [
...         datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...         datetime(2025, 1, 1, 12, 0, 1, tzinfo=timezone.utc),
...         datetime(2025, 1, 1, 12, 0, 2, tzinfo=timezone.utc),
...     ]
... )
>>> path = Path(
...     [
...         Point([1, 2, 0], time=times[0]),
...         Point([2, 3.8, 0.4], time=times[1]),
...         Point([3, 6.0, 1], time=times[2]),
...     ]
... )  # Somewhere very hot in the middle of the Earth
>>> vel = path.vel
>>> vel.magnitude  # Velocity magnitudes in m/s
array([1.91049732, 2.29128785, 2.6925824])
>>> vel.unit  # Unit velocity vectors
array([[0.52342392, 0.83747828, 0.15702718],
       [0.43643578, 0.87287156, 0.21821789],
       [0.37139068, 0.89133762, 0.25997347]])
classmethod from_data(vec, time, backend=None)[source]

Create a Velocity object from velocity vector array and Time object.

Return type:

Velocity

Parameters:
  • vec (Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str])

  • time (TimeLike)

  • backend (str | Any | None)

convert_to(backend)[source]

Convert the Velocity object to a different backend.

Return type:

Velocity

Parameters:

backend (str | Any | None)

property backend: str
property magnitude: Any

Get the velocity magnitude in m/s.

time: TimeLike
property unit: Any

Get the unit velocity vector.

vec: Any