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.
- positions
sargeom.coordinates.CartesianECEF
orsargeom.coordinates.Cartographic
Array of positions in either ECEF (x, y, z) or geographic (latitude, longitude, altitude) format.
- orientations
scipy.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, 3]) >>> 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, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.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], [270, 0, 0]], degrees=True) >>> traj = Trajectory(timestamps, positions, orientations)
Attributes Summary
Arc lengths between consecutive trajectory positions.
Orientations of the trajectory samples, in the NED frame.
Positions of the trajectory samples, in ECEF coordinates.
Sampling rate of the trajectory.
Timestamps of the trajectory samples, in seconds.
Velocities between consecutive trajectory samples.
Methods Summary
concatenate
(trajectories)Concatenate a sequence of Trajectory objects into a single object.
from_numpy
(data)Create a Trajectory instance from a numpy structured array.
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.
Convert the Trajectory instance to a pandas DataFrame.
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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> traj.arc_lengths array([85584.58995186, 67305.32205239, 69307.98527392])
- 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], [270, 0, 0]], degrees=True) >>> traj = Trajectory( ... timestamps=[0, 1, 2, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.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], [0.00000000e+00, 0.00000000e+00, 7.07106781e-01, -7.07106781e-01]])
- 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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> traj.positions XYZ CartesianECEF points [[4614831.06382533 312803.18870294 4377307.25608437] [4583825.9258778 388064.96749322 4403747.15229078] [4610933.91105407 440116.57314554 4370795.76589696] [4584879.02442076 500870.74890955 4391620.5067715 ]]
- 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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> traj.timestamps array([0, 1, 2, 3])
- 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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> traj.velocities array([85584.58995186, 67305.32205239, 69307.98527392])
Methods Documentation
- classmethod concatenate(trajectories)[source]¶
Concatenate a sequence of Trajectory objects into a single object.
- Parameters:
- trajectoriessequence of
sargeom.Trajectory
The trajectories to concatenate. Can be a list, tuple, or any iterable.
- trajectoriessequence of
- Returns:
sargeom.Trajectory
A new Trajectory instance containing the concatenated data.
- Raises:
ValueError
If the input list is empty.
If any item in the list is not a Trajectory instance.
Notes
Timestamps and positions are always concatenated.
Orientations are concatenated only if ALL input trajectories have orientations.
If any trajectory lacks orientations, the result will have no orientations.
The order of concatenation follows the order of input trajectories.
Examples
Concatenate two trajectories with orientations:
>>> import numpy as np >>> from scipy.spatial.transform import Rotation >>> timestamps_1 = np.array([0.0, 1.0]) >>> positions_1 = CartesianECEF(x=[1000, 1100], y=[2000, 2100], z=[3000, 3100]) >>> orientations_1 = Rotation.from_euler("ZYX", [[0, 0, 0], [10, 0, 0]], degrees=True) >>> traj_1 = Trajectory(timestamps_1, positions_1, orientations_1) >>> >>> timestamps_2 = np.array([2.0, 3.0]) >>> positions_2 = CartesianECEF(x=[1200, 1300], y=[2200, 2300], z=[3200, 3300]) >>> orientations_2 = Rotation.from_euler("ZYX", [[20, 0, 0], [30, 0, 0]], degrees=True) >>> traj_2 = Trajectory(timestamps_2, positions_2, orientations_2) >>> >>> combined = Trajectory.concatenate([traj_1, traj_2]) >>> len(combined) 4 >>> combined.has_orientation() True
Concatenate trajectories without orientations:
>>> traj_no_orient_1 = Trajectory(timestamps_1, positions_1) >>> traj_no_orient_2 = Trajectory(timestamps_2, positions_2) >>> combined_no_orient = Trajectory.concatenate([traj_no_orient_1, traj_no_orient_2]) >>> combined_no_orient.has_orientation() False
Concatenate single trajectory with multiple points:
>>> single_position = Cartographic(longitude=2.0, latitude=46.0, height=0.0) >>> single_traj = Trajectory(timestamps=[10.0], positions=single_position) >>> multi_positions = Cartographic( ... longitude=[3.0, 4.0, 5.0], ... latitude=[47.0, 48.0, 49.0], ... height=[100.0, 200.0, 300.0] ... ) >>> multi_traj = Trajectory(timestamps=[11.0, 12.0, 13.0], positions=multi_positions) >>> result = Trajectory.concatenate([single_traj, multi_traj]) >>> len(result) 4
- static from_numpy(data)[source]¶
Create a Trajectory instance from a numpy structured array.
- Parameters:
- data
numpy.ndarray
Input data as a numpy structured array using the
TRAJ_DTYPE
type.
- data
- 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.), ... (3., 6.2345, 43.7891, 600., 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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> interp_traj = traj.interp([0.5, 1.5, 2.5])
- classmethod read_csv(filename)[source]¶
Read a TRAJ CSV file and create a Trajectory instance.
- Parameters:
- filename
str
orpathlib.Path
The filename or path to the .traj.csv file.
- filename
- 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.
- classmethod read_pamela_pos(filename)[source]¶
Read a PAMELA .pos file and create a Trajectory instance.
- Parameters:
- filename
str
orpathlib.Path
The filename or path to the .pos file.
- filename
- Returns:
Trajectory
A new Trajectory instance.
- Raises:
FileNotFoundError
If the file does not exist.
ValueError
If the file does not have a .pos extension.
- classmethod read_pamela_traj(filename, sampling_time_s=None, crs='auto')[source]¶
Read a PAMELA .traj file and create a Trajectory instance.
- Parameters:
- filename
str
orpathlib.Path
The filename or path to the .traj file.
- sampling_time_s
float
, optional If provided, overrides the time step between trajectory points (in seconds).
- crs
str
, optional Coordinate reference system of the trajectory. Options are: ‘auto’ (default, WGS84 if new format, NTF if old format), ‘WGS84’, ‘NTF’.
- filename
- 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.
ValueError
If the crs parameter is not one of the accepted values.
- resample(sampling_rate)[source]¶
Resample the trajectory at a specified sampling rate.
- Parameters:
- sampling_rate
float
The desired sampling rate in Hz.
- sampling_rate
- 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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> resampled_traj = traj.resample(2.0)
- save_csv(filename)[source]¶
Save the Trajectory instance to a TRAJ CSV file.
- Parameters:
- filename
str
orpathlib.Path
The filename or path to save the .traj.csv file.
- filename
- Returns:
pathlib.Path
The path to the saved .traj.csv file.
Examples
>>> traj = Trajectory( ... timestamps=[0, 1, 2, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> filename = traj.save_csv("output") >>> print(filename) output.traj.csv
- save_npy(filename)[source]¶
Save the Trajectory instance to a numpy .npy file.
- Parameters:
- filename
str
orpathlib.Path
The filename or path to save the .npy file.
- filename
- Returns:
pathlib.Path
The path to the saved .npy file.
Examples
>>> traj = Trajectory( ... timestamps=[0, 1, 2, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> filename = traj.save_npy("output") >>> print(filename) output.npy
- save_pamela_pos(filename)[source]¶
Save the Trajectory instance to a PAMELA .pos file.
- Parameters:
- filename
str
orpathlib.Path
The filename or path to save the .pos file.
- filename
- Returns:
pathlib.Path
The path to the saved .pos file.
Examples
>>> traj = Trajectory( ... timestamps=[0, 1, 2, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> filename = traj.save_pamela_pos("output") >>> print(filename) output.pos
- sort(inplace=True, reverse=False)[source]¶
Sort the trajectory by timestamps.
- Parameters:
- Returns:
Trajectory
or NoneThe sorted Trajectory instance if inplace is False, otherwise None.
Examples
Sort the trajectory in place:
>>> traj = Trajectory( ... timestamps=[2, 0, 1, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.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.) (3., 6.2345, 43.7891, 600., 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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> traj.to_numpy() 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.), (3., 6.2345, 43.7891, 600., 0., 0., 0.)], dtype=[('TIMESTAMP_S', '<f8'), ('LON_WGS84_DEG', '<f8'), ('LAT_WGS84_DEG', '<f8'), ('HEIGHT_WGS84_M', '<f8'), ('HEADING_DEG', '<f8'), ('ELEVATION_DEG', '<f8'), ('BANK_DEG', '<f8')])
- 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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> traj.to_pandas() TIMESTAMP_S LON_WGS84_DEG LAT_WGS84_DEG HEIGHT_WGS84_M HEADING_DEG ELEVATION_DEG BANK_DEG 0 0.0 3.8777 43.6135 300.0 0.0 0.0 0.0 1 1.0 4.8391 43.9422 400.0 0.0 0.0 0.0 2 2.0 5.4524 43.5309 500.0 0.0 0.0 0.0 3 3.0 6.2345 43.7891 600.0 0.0 0.0 0.0
- 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, 3], ... positions=Cartographic( ... longitude=[3.8777, 4.8391, 5.4524, 6.2345], ... latitude=[43.6135, 43.9422, 43.5309, 43.7891], ... height=[300.0, 400.0, 500.0, 600.0] ... ) ... ) >>> traj.total_arc_length() np.float64(231835.03546103595)