Trajectory

class sargeom.Trajectory(timestamps, positions, orientations=None)[source]

Bases: object

A Trajectory object represents a sequence of positions and orientations over time.

It is defined by the following characteristics:

  • Timestamps are expressed in seconds. They may correspond to UTC, GPS Seconds of Week (SOW), Time of Day (TOD), or a custom time reference.

  • Positions are provided in either the WGS84 geographic coordinate system (EPSG:4979) or the WGS84 geocentric coordinate system (EPSG:4978).

  • Orientations are defined in the local North-East-Down (NED) Cartesian frame, relative to the associated position coordinates.

Parameters:
timestampsarray_like

1D array of timestamps corresponding to each trajectory sample.

positionssargeom.coordinates.CartesianECEF or sargeom.coordinates.Cartographic

Array of positions in either ECEF (x, y, z) or geographic (latitude, longitude, altitude) format.

orientationsscipy.spatial.transform.Rotation, optional

Sequence of orientations as Rotation objects. Defined in the NED frame. Default is None.

Raises:
TypeError
  • If positions are not of type CartesianECEF or Cartographic.

  • If orientations are provided but not of type scipy.spatial.transform.Rotation.

Examples

Create a Trajectory instance using ECEF (Cartesian) coordinates:

>>> timestamps = np.array([0, 1, 2])
>>> positions = CartesianECEF(
...     x=[4614831.06382533, 4583825.9258778, 4610933.91105407],
...     y=[312803.18870294, 388064.96749322, 440116.57314554],
...     z=[4377307.25608437, 4403747.15229078, 4370795.76589696]
... )
>>> traj = Trajectory(timestamps, positions)

Create a Trajectory instance using geographic (Cartographic) coordinates:

>>> positions = Cartographic(
...     longitude=[3.8777, 4.8391, 5.4524],
...     latitude=[43.6135, 43.9422, 43.5309],
...     height=[300.0, 400.0, 500.0]
... )
>>> traj = Trajectory(timestamps, positions)

Create a Trajectory instance with orientations:

>>> from scipy.spatial.transform import Rotation
>>> orientations = Rotation.from_euler("ZYX", [[0, 0, 0], [90, 0, 0], [180, 0, 0]], degrees=True)
>>> traj = Trajectory(timestamps, positions, orientations)

Attributes Summary

arc_lengths

Arc lengths between consecutive trajectory positions.

orientations

Orientations of the trajectory samples, in the NED frame.

positions

Positions of the trajectory samples, in ECEF coordinates.

sampling_rate

Sampling rate of the trajectory.

timestamps

Timestamps of the trajectory samples, in seconds.

velocities

Velocities between consecutive trajectory samples.

Methods Summary

from_numpy(data)

Create a Trajectory instance from a numpy structured array.

has_orientation()

Whether the trajectory has orientation data.

interp(new_timestamps)

Interpolate the trajectory to new timestamps.

plot(**kwargs)

read_csv(filename)

Read a TRAJ CSV file and create a Trajectory instance.

read_pamela_pos(filename)

Read a PAMELA .pos file and create a Trajectory instance.

read_pamela_traj(filename)

Read a PAMELA .traj file and create a Trajectory instance.

read_pivot(filename)

resample(sampling_rate)

Resample the trajectory at a specified sampling rate.

save_csv(filename)

Save the Trajectory instance to a TRAJ CSV file.

save_kml(filename, **kwargs)

save_npy(filename)

Save the Trajectory instance to a numpy .npy file.

save_pamela_pos(filename)

Save the Trajectory instance to a PAMELA .pos file.

save_pivot(filename)

sort([inplace, reverse])

Sort the trajectory by timestamps.

to_numpy()

Convert the Trajectory instance to a numpy structured array.

to_pandas()

Convert the Trajectory instance to a pandas DataFrame.

total_arc_length()

Compute the total arc length of the trajectory.

Attributes Documentation

arc_lengths

Arc lengths between consecutive trajectory positions.

Returns:
numpy.ndarray

1D array of distances in meters.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.arc_lengths
array([85584.58995186, 67305.32205239])
orientations

Orientations of the trajectory samples, in the NED frame.

Returns:
scipy.spatial.transform.Rotation

Sequence of orientations as a Rotation object.

Raises:
ValueError

If the trajectory has no orientations.

Notes

The orientations are defined in the local North-East-Down (NED) Cartesian frame, relative to the associated position coordinates. The orientations can be converted to quaternions, Euler angles, or other representations using the methods provided by the Rotation class.

Examples

>>> from scipy.spatial.transform import Rotation
>>> attitude = Rotation.from_euler("ZYX", [[0, 0, 0], [90, 0, 0], [180, 0, 0]], degrees=True)
>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     ),
...     orientations=attitude
... )
>>> traj.orientations.as_quat()
array([[0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00],
       [0.00000000e+00, 0.00000000e+00, 7.07106781e-01, 7.07106781e-01],
       [0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 6.12323400e-17]])
positions

Positions of the trajectory samples, in ECEF coordinates.

Returns:
sargeom.coordinates.CartesianECEF

3D positions expressed in the WGS84 geocentric frame (EPSG:4978).

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.positions
XYZ CartesianECEF points
[[4614831.06382533  312803.18870294 4377307.25608437]
 [4583825.9258778   388064.96749322 4403747.15229078]
 [4610933.91105407  440116.57314554 4370795.76589696]]
sampling_rate

Sampling rate of the trajectory.

Returns:
float

Sampling frequency in Hz.

Raises:
ValueError

If there are fewer than two timestamps.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.sampling_rate
np.float64(1.0)
timestamps

Timestamps of the trajectory samples, in seconds.

Returns:
numpy.ndarray

1D array of timestamps (e.g., UTC, GPS SOW, TOD).

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.timestamps
array([0, 1, 2])
velocities

Velocities between consecutive trajectory samples.

Returns:
numpy.ndarray

1D array of velocity magnitudes, in meters per second.

Raises:
ValueError

If there are fewer than two timestamps.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.velocities
array([85584.58995186, 67305.32205239])

Methods Documentation

static from_numpy(data)[source]

Create a Trajectory instance from a numpy structured array.

Parameters:
datanumpy.ndarray

Input data as a numpy structured array using the TRAJ_DTYPE type.

Returns:
Trajectory

A new Trajectory instance.

Raises:
TypeError

If the input data is not a numpy array.

ValueError

If the input data does not have the correct dtype.

Examples

>>> data = np.array([
...     (0., 3.8777, 43.6135, 300., 0., 0., 0.),
...     (1., 4.8391, 43.9422, 400., 0., 0., 0.),
...     (2., 5.4524, 43.5309, 500., 0., 0., 0.)
... ], dtype=TRAJ_DTYPE)
>>> traj = Trajectory.from_numpy(data)
has_orientation()[source]

Whether the trajectory has orientation data.

Returns:
bool

True if orientations are defined, False otherwise.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.has_orientation()
False
interp(new_timestamps)[source]

Interpolate the trajectory to new timestamps.

Parameters:
new_timestampsarray_like

Array of new timestamps to interpolate the trajectory to.

Returns:
Trajectory

A new Trajectory instance with interpolated data.

Raises:
ValueError

If the new timestamps are not within the range of existing timestamps.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> interp_traj = traj.interp([0.5, 1.5])
plot(**kwargs)[source]
read_csv(filename)[source]

Read a TRAJ CSV file and create a Trajectory instance.

Parameters:
filenamestr or pathlib.Path

The filename or path to the .traj.csv file.

Returns:
Trajectory

A new Trajectory instance.

Raises:
FileNotFoundError

If the file does not exist.

ValueError

If the file does not have a .traj.csv extension.

read_pamela_pos(filename)[source]

Read a PAMELA .pos file and create a Trajectory instance.

Parameters:
filenamestr or pathlib.Path

The filename or path to the .pos file.

Returns:
Trajectory

A new Trajectory instance.

Raises:
FileNotFoundError

If the file does not exist.

ValueError

If the file does not have a .pos extension.

read_pamela_traj(filename)[source]

Read a PAMELA .traj file and create a Trajectory instance.

Parameters:
filenamestr or pathlib.Path

The filename or path to the .traj file.

Returns:
Trajectory

A new Trajectory instance.

Raises:
FileNotFoundError

If the file does not exist.

ValueError

If the file does not have a .traj extension.

ValueError

If the old PAMELA file format is detected.

read_pivot(filename)[source]
resample(sampling_rate)[source]

Resample the trajectory at a specified sampling rate.

Parameters:
sampling_ratefloat

The desired sampling rate in Hz.

Returns:
Trajectory

A new Trajectory instance with resampled data.

Raises:
TypeError

If the sampling rate is not a number.

ValueError

If the sampling rate is not positive.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> resampled_traj = traj.resample(2.0)
save_csv(filename)[source]

Save the Trajectory instance to a TRAJ CSV file.

Parameters:
filenamestr or pathlib.Path

The filename or path to save the .traj.csv file.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.save_csv("output.traj.csv")
save_kml(filename, **kwargs)[source]
save_npy(filename)[source]

Save the Trajectory instance to a numpy .npy file.

Parameters:
filenamestr or pathlib.Path

The filename or path to save the .npy file.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.save_npy("output.npy")
save_pamela_pos(filename)[source]

Save the Trajectory instance to a PAMELA .pos file.

Parameters:
filenamestr or pathlib.Path

The filename or path to save the .pos file.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.save_pamela_pos("output.pos")
save_pivot(filename)[source]
sort(inplace=True, reverse=False)[source]

Sort the trajectory by timestamps.

Parameters:
inplacebool, optional

If True, sort the trajectory in place. Default is True.

reversebool, optional

If True, sort in descending order. Default is False.

Returns:
Trajectory or None

The sorted Trajectory instance if inplace is False, otherwise None.

Examples

Sort the trajectory in place:

>>> traj = Trajectory(
...     timestamps=[2, 0, 1],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.sort()
Trajectory samples (t, x, y, z, h, e, b)
[(0., 4.8391, 43.9422, 400., 0., 0., 0.)
 (1., 5.4524, 43.5309, 500., 0., 0., 0.)
 (2., 3.8777, 43.6135, 300., 0., 0., 0.)]

Sort the trajectory and return a new instance:

>>> sorted_traj = traj.sort(inplace=False)
to_numpy()[source]

Convert the Trajectory instance to a numpy structured array.

Returns:
numpy.ndarray

The trajectory data as a numpy structured array using the TRAJ_DTYPE type.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> data = traj.to_numpy()
to_pandas()[source]

Convert the Trajectory instance to a pandas DataFrame.

Returns:
pandas.DataFrame

The trajectory data as a pandas DataFrame.

Raises:
ModuleNotFoundError

If pandas is not installed.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> df = traj.to_pandas()
total_arc_length()[source]

Compute the total arc length of the trajectory.

Returns:
float

Sum of distances between consecutive positions, in meters.

Examples

>>> traj = Trajectory(
...     timestamps=[0, 1, 2],
...     positions=Cartographic(
...         longitude=[3.8777, 4.8391, 5.4524],
...         latitude=[43.6135, 43.9422, 43.5309],
...         height=[300.0, 400.0, 500.0]
...     )
... )
>>> traj.total_arc_length()
np.float64(152889.9120042545)