Basic Classes

Domains (Coordinate Systems)

Tracktable operates on points, timestamps and trajectories. Since points and trajectories are meaningless without a coordinate system, we instantiate points and trajectories from a domain. Each domain provides several different data types and a standard set of units. By design, it is difficult to mix points and trajectories from different domains. While we cannot prevent you entirely from mixing up (for example) kilometers and miles when computing distances, we can at least try to make it difficult.

Tracktable includes the following domains:

Available Point Domains

Python Module

Description

tracktable.domain.terrestrial

Points in longitude/latitude space

tracktable.domain.cartesian2d

Points in flat 2D space

tracktable.domain.cartesian3d

Points in flat 3D space

tracktable.domain.feature_vectors

Collection of points in Cartesian space with 2 to 30 dimensions

Each domain defines several data types:

Domain Data Types

Python Class

Description

BasePoint

Bare point - just coordinates.

TrajectoryPoint

Point with coordinates, object ID, timestamp and user-defined properties.

Trajectory

Vector of trajectory points. Trajectories have their own user-defined properties.

LineString

Vector of un-decorated points (base points).

Box

Axis-aligned bounding box.

BasePointReader

Read BasePoints from a delimited text file.

BasePointWriter

Write BasePoints to a delimited text file.

TrajectoryPointReader

Read TrajectoryPoints from a delimited text file.

TrajectoryPointWriter

Write TrajectoryPoints to a delimited text file.

TrajectoryReader

Read Trajectories from a delimited text file.

TrajectoryWriter

Write Trajectories to a delimited text file.

We provide rendering support for the terrestrial and 2D Cartesian domains via Matplotlib and Cartopy. Rendering support for 3D points and trajectories is still an open issue. Given the limited support for 3D data in Matplotlib we may delegate this job to another library. Exactly which library we might choose is open for discussion.

Timestamp

There is a single timestamp class that is common across all domains. This is a timezone-aware datetime.datetime.

The tracktable.core.Timestamp class contains several convenience methods for manipulating timestamps. A full list is in the timestamp reference documentation. We use the following ones most frequently.

  • Timestamp.from_any: Try to convert whatever argument we supply into a timestamp. The input can be a dict, a datetime, a string in the format YYYY-MM-DD HH:MM:SS or YYYY-MM-DD HH:MM:SS+ZZ (for a time zone offset from UTC).

  • Timestamp.to_string: Convert a timestamp into its string representation. By default this will produce a string like 2014-08-28 13:23:45. Optional arguments to the function will allow us to change the output format and include a timezone indicator.

Point Classes

Base Points

Within a domain, Tracktable uses the BasePoint class to store a bare set of (lon, lat) coordinates. These BasePoint coordinates behave like lists in that we use square brackets, [], to set and get coordinates. For example:

 1from tracktable.domain.terrestrial import BasePoint
 2
 3my_point = BasePoint()
 4my_point[0] = my_longitude
 5my_point[1] = my_latitude
 6
 7# You could also assign the coordinates in the constructor:
 8# my_point = BasePoint(my_longitude, my_latitude)
 9
10longitude = my_point[0]
11latitude = my_point[1]

Longitude is always coordinate 0 and latitude is always coordinate 1. We choose this ordering for consistency with the 2D Cartesian domain where the X coordinate is always at position 0 and the Y coordinate is at position 1.

Trajectory Points

For assembling trajectories in a given domain, Tracktable uses the TrajectoryPoint class to store the (lon, lat) coordinates as well as additional point information such as the timestamp and object_id.

Each TrajectoryPoint has the following properties:

  1. Coordinates (inherited from BasePoint)

  2. An identifier for the moving object

  3. A timestamp for this observation of the object’s position

The following code shows how to initialize a trajectory point:

 1from tracktable.domain.terrestrial import TrajectoryPoint
 2from tracktable.core import Timestamp
 3
 4longitude = 50
 5latitude = 40
 6
 7my_point = TrajectoryPoint()
 8my_point[0] = longitude
 9my_point[1] = latitude
10
11# As with BasePoint, you can also set coordinates in the
12# constructor:
13# my_point = TrajectoryPoint(longitude, latitude)
14
15my_point.object_id = 'FlightId'
16my_point.timestamp = Timestamp.from_any('2014-04-05 13:25:00')

You may want to associate other data with a point as well. For example, aircraft trajectories often have altitude and information about the flight’s origin and destination:

1my_point.properties['altitude'] = 13400
2my_point.properties['origin'] = 'ORD'
3my_point.properties['destination'] = 'LAX'
4my_point.properties['departure_time'] = Timestamp.from_any('2015-02-01 18:00:00')

For the most part you can treat the properties array like a Python dict. However, it can only hold values that are of float, string or Timestamp type.

LineStrings

We include LineString for ordered sequences of points. LineString is analogous to BasePoint in that it has no decoration (as in no additional data that is unrelated to the points longitude and latitude) at all. It is just a sequence of points.

1 from tracktable.domain.terrestrial import BasePoint
2
3 point_one = BasePoint(50, 40)
4 point_two = BasePoint(60, 40)
5
6 linestring = []
7 linestring.append(point_one)
8 linestring.append(point_two)

Trajectories

We provide Trajectory for ordered sequences of points. Trajectory has its own ID (trajectory_id) as well as its own properties array. You do not need to supply a trajectory ID: it is computed automatically from the object ID, start time, and end time.

As with the point classes above, each domain in Tracktable defines a trajectory class. Trajectories can be treated like Python lists: indexing, slicing, insertion and removal all work as they do with an ordinary Python list.

Example: Assemble Trajectory from Points
 1# Populate a trajectory from scratch
 2from tracktable.domain.terrestrial import Trajectory, TrajectoryPoint
 3import datetime
 4
 5point1 = TrajectoryPoint(50, 40)
 6point1.object_id = "A"
 7point1.timestamp = datetime.datetime.now()
 8
 9point2 = TrajectoryPoint(60, 30)
10point2.object_id = "A"
11point2.timestamp = point1.timestamp + datetime.timedelta(hours=3)
12
13traj = Trajectory()
14traj.append(point1)
15traj.append(point2)
Assemble Trajectory from List of Points
1from tracktable.domain.terrestrial import Trajectory
2
3# Assume that 'mypoints' is a list of TrajectoryPoints that you
4# have populated somewhere else in your code. You can create
5# a Trajectory from that list in one call:
6
7traj = Trajectory.from_position_list(mypoints)

Note

Tracktable expects that all points in a given trajectory will have the same object ID. Timestamps must not decrease from one point to the next.

There are several free functions defined on trajectories that do useful things. We expect that the following will be used most often:

  • point_at_time(trajectory: Trajectory, when: Timestamp): Given a timestamp, interpolate between points on the trajectory to find the point at exactly the specified time. Timestamps before the beginning or after the end of the trajectory will return the start and end points, respectively. Tracktable will try to interpolate all properties that are defined on the trajectory points.

  • subset_during_interval(trajectory: Trajectory, start, end: Timestamp): Given a start and end timestamp, extract the subset of the trajectory between those two times. The start and end points will be at exactly the start and end times you specify. These will be interpolated if there are no points in the trajectory at precisely the right time. Points in between the start and end times will be copied from the trajectory without modification.

  • recompute_speed(trajectory: Trajectory, target_attribute_name='speed'): Compute new values for the speed numeric property at each point given the position and timestamp attributes. These are convenient if our original data set lacks speed information or if the original values are corrupt.