Skip to main content

Toolbox for extracting trajectories and monitoring vessels from raw AIS records.

Project description

Python Trajectory Search Agent (PyTSA) for raw AIS records

This module provides a set of functionalities around Automatic Identification System (AIS) messages, such as

  • Decoding raw AIS messages
  • Extracting clean, practical and interpolated trajectories from the data, based on various (also user-defined) filters.
  • Providing an easy-to-use interface for observing target ships and their state around a given time and position.

Motivation

Simulation studies in maritime contexts often lack an easy-to-use model for vessel traffic extraction around a simulated vessel, as the large amounts of AIS records make it challenging and time-consuming to pinpoint the exact vessels to be monitored.

Also, for validating path-following, or collision avoidance systems, it is beneficial to use real-world trajectories, as they often provide a larger variety of movement patterns than simulated trajectories. However, the exact process of extracting the relevant information from the raw AIS data is often not sufficiently documented, thus making it difficult to reproduce the results.

Therefore, this module aims to provide a unified, open-source solution for extracting relevant trajectories from raw AIS data, as well as providing an easy-to-use interface for observing target ships around a given position and time.

Installation

Install the package via pip:

$ pip install pytsa-ais

Usage

Raw AIS data

One file of raw AIS records must only contain dynamic AIS messages (Types 1,2,3 and 18) or static AIS messages (Type 5). A combination of both is not supported. The data must be provided in the .csv format and must be named YYYY_MM_DD.csv. Other file names are not supported.

This is done to avoid extra-day sorting of the data, which would be necessary if the data was not sorted by date. Intra-day sorting is done regardless of the file name.

Individual files must contain the following columns:

  • timestamp: ISO 8601 parseable date format (e.g. "2021-07-03T00:00:00.000Z")
  • message_id: AIS message type (1,2,3,5,18)

For dynamic AIS messages (Types 1,2,3,18) additionally

  • raw_message: For messages of type 1,2,3,18, the raw message consists of a single AIVDM sentence.

For static AIS messages (Type 5) additionally:

  • raw_message1: First AIVDM sentence
  • raw_message2: Second AIVDM sentence

Example Table for dynamic AIS messages

timestamp message_id raw_message
2021-07-03T00:00:00.000Z 1 "!ABVDM,1,1,,B,177PhT001iPWhwJPsK=9DoQH0<>i,0*7C"

Example Table for static AIS messages

timestamp message_id raw_message1 raw_message2
2021-07-03T00:00:00.000Z 5 "!ABVDM,2,1,5,A,53aQ5aD2;PAQ0@8l000lE9LD8u8L00000000001??H<886?80@@C1F0CQ4R@,0*35" "!ABVDM,2,2,5,A,@0000000000,2*5A"

For more information on the AIS message structure, see here.

Decoding AIS messages

Once your raw AIS data is in the correct format, you can decode the AIS messages by calling the decode() function. The function takes as arguments the path to a directory containing the raw AIS data, as well as the path to the output directory. The function will then decode all .csv files in the input directory and save the decoded data to the output directory under the same file name.

from pytsa import decode

decode(
    source = "path/to/raw_dir",
    dest = "path/to/decoded_dir",
    njobs = 1
)

For decoding AIS messages, you can choose between single-processing and multi-processing decoding. The multi-processing decoding is recommended for large datasets containing multiple files, as it is significantly faster than single-process decoding. However, during decoding, the files are loaded into memory in their entirety, which may lead to memory issues for large datasets or a large number of jobs. Therefore, it is recommended to use single-processing decoding for smaller datasets or if you encounter memory issues. Parallel decoding may also not be avialable on Windows systems (due to the lack of testing on Windows systems, this is not guaranteed, sorry...)

Decoded AIS data

In case you already have decoded AIS messages, you have to make sure, that the fields of your .csv file at least partially match Msg12318Columns and Msg5Columns at pytsa/decode/filedescriptor.py.

In case you have a different data structure, you can either adapt the Msg12318Columns and Msg5Columns classes, or you can adapt the column names of your .csv file to match the column names of the Msg12318Columns and Msg5Columns classes.

Using the SearchAgent for extracting target ships

The central object of the module is the SearchAgent class, which provides an easy-to-use interface for extracting target ships around a given position and time.

Possible applications include:

  • Tracking traffic around a simulated route
  • Monitoring traffic around a fixed location
  • Extracting trajectories

The Search Agent must be instantiated with three components: Its BoundingBox, msg12318files and msg5files:

  • BoundingBox: Reference frame containing the spatial extent of the searchable area in degrees of latitude and longitude.

  • msg12318files: File path to a .csv file containing decoded dynamic AIS messages (Types 1,2,3 and 18 only) to consider for the search procedure. See the next section for details on the data structure.

  • msg5files: File path to the corresponding .csv file containing decoded static AIS messages (message type 5)

Example instantiation for a small area in the North Sea:

import pytsa
from pathlib import Path

# Lat-Lon Box with [lat,lon, SOG, COG] outputs
frame = pytsa.BoundingBox(
    LATMIN = 52.2, # [°N]
    LATMAX = 56.9, # [°N]
    LONMIN = 6.3,  # [°E]
    LONMAX = 9.5,  # [°E]
)

dynamic_data = Path("/path/to/dynamic.csv")
static_data = Path("/path/to/static.csv")

search_agent = pytsa.SearchAgent(
    msg12318file = dynamic_data,
    msg5file = static_data
    frame = frame
)

Monitoring vessel traffic around a given position

To commence a search for ships around a given location, it is mandatory to use a TimePosition object to store the position and time at which the search shall be commenced simultaneously. Example:

from pytsa import TimePosition

tpos = TimePosition(
    timestamp="2021-07-03T12:03:00.000Z",
    lat=52.245,
    lon=9.878
)

After defining a TimePosition, a search can be commenced by freezing the search agent at the given position and time

target_ships = search_agent.freeze(tpos)

yielding a list of TargetShip objects (see pytsa/targetship.py for more information).

By default, the resulting TargetShip objects used linear interpolation to estimate the current position, speed and course of the target ships. If instead, cubic spline interpolation is desired, the interpolation option can be set to spline. Additionally, the search_radius can be set to a custom value in nautical miles.

target_ships = search_agent.freeze(
    tpos, 
    interpolation="spline", 
    search_radius=5 # [nm]
)

To get the current Latitude, Longitude, SOG, COG for each TargetShip object at the provided timestamp, the observe() method can be used, returning a numpy array with the current position, speed and course.

for ship in target_ships:
    ship.observe()

# Example output for one ship
# 
# Interpolated COG ---------------
# Interpolated SOG -----------    |
# Interpolated Longitude-|   |    |
# Interpolated Latitude  |   |    |
#                v       v   v    v
>>> np.array([52.232,9.847,12.34,223.4])

Full example

import pytsa
from pathlib import Path

# Global geographic search area.
# Outside these bounds, no search will be commenced
frame = pytsa.BoundingBox(
    LATMIN = 52.2, # [°N]
    LATMAX = 56.9, # [°N]
    LONMIN = 6.3,  # [°E]
    LONMAX = 9.5,  # [°E]
)

# File containing AIS messages
dynamic_data = Path("/path/to/dynamic.csv")
static_data = Path("/path/to/static.csv")

# Instantiate the search agent with the source file 
# and the search area
search_agent = pytsa.SearchAgent(
    msg12318file = dynamic_data,
    msg5file = static_data
    frame = frame
)

# Provide a position and time for which the search
# will be carried out
tpos = pytsa.TimePosition(
    timestamp="2021-07-03T12:03:00.000Z",
    lat=52.245,
    lon=9.878
)

# Search for TargetVessels with 
# default settings: 
#   Linear interpolation, 
#   20 nm search radius
target_ships = search_agent.freeze(tpos)

# Extract the current position, speed and
# course for all found target vessels.
for ship in target_ships:
    ship.observe()

# Example output for one ship
>>> np.array([52.232,9.847,12.34,223.4])

Extracting trajectories

If instead of observing target ships around a given position, you want to extract trajectories from the data, you can use the SearchAgent.extract_all().

By default, the extract_all() method walks through the entire dataset and extracts all trajectories that are within the search area utilizing the split-point approach from Section 4 in our original paper. The method returns a dictionary with the MMSI as keys and the corresponding TargetShip objects as values.

all_ships = search_agent.extract_all()

To skip the split-point approach you can set the skip_tsplit parameter to True. This will result in TargetShip objects that only contain a single trajectory, which is the raw, time-ordered set of AIS messages for the given MMSI.

all_ships = search_agent.extract_all(skip_tsplit=True)

The extract_all() method used 4-core parallel processing by default. This can be adjusted by setting the njobs parameter to a custom value. Note, that high njobs values may lead to a slowdown due to the overhead of splitting the data into chunks and reassembling the results.

The trajectories of each TargetShip object can be accessed by the tracks attribute, which is of type list[Track]. Each Track within the tracks list contains the AIS messages for a single trajectory. See the pytsa.structs.AISMessage module for more information on the fields of the AIS messages.

# Example for printing the positions for each trajectory
for ship in all_ships.values():
    for track in ship.tracks:
        for msg in track:
            print(msg.lat, msg.lon)

The trajectories extracted via the extract_all() method are not interpolated by default. To manually interpolate them, you can use the interpolate() method of the TargetShip object.

for ship in all_ships.values():
    ship.interpolate(mode="linear") # or "spline"

Refer also to the function documentation for further details.

Refining trajectories using the Inspector class

Once the TargetShips with its trajectories are extracted, PyTSA provides a flexible interface for refining the trajectories using the Inspector class. The output of the Inspector is two dictionaries [accepted,rejected], of type dict[MMSI,TargetShip]. The first dictionary contains the TargetShip objects that passed the inspection, while the second dictionary contains the TargetShip objects that failed the inspection.

Note: It is possible that the same MMSI is present in both dictionaries. If so, the TargetShip object in the rejected dictionary will contain only rejected trajectories, while the TargetShip object in the accepted dictionary will contain only accepted trajectories.

The Inspector works with a set of rules, that must be combined into a Recipe object, which is then passed to the Inspector object.

Before we show an example, let's explain the concept of rules and recipes:

Rules

A rule is a function following the signature rule(track: Track) -> bool. It takes a single Track object as input and returns a boolean value. Rules are set to act as a negative filter, meaning that if a rule returns True, the corresponding Track will be removed from the TargetShip object.

It is possible for rules to have more than one argument, like rule(track: Track, *args, **kwargs) -> bool, however, for constructing a recipe, all other arguments must be pre-set, for example by using a lambda function, or the functools.partial function.

A simple rule that removes all tracks with less than 10 AIS messages would look like this:

from pytsa import Track

def track_too_short(track: Track) -> bool:
    return len(track) < 10

A rule filtering trajectories whose latitude is outside given bounds could look like this:

def lat_outside_bounds(track: Track, latmin: float, latmax: float) -> bool:
    return any([msg.lat < latmin or msg.lat > latmax for msg in track])

Feel free to define your own rules, or use the ones provided in the pytsa.trajectories.rules module.

Recipes

A recipe is a list of rules that are combined into a single function using the Recipe class.

from pytsa import Recipe
from fuctools import partial

# Create a recipe with the 
# two rules defined above.
recipe = Recipe(
    track_too_short,
    partial(lat_outside_bounds, latmin=52.2, latmax=56.9)
)

Applying the recipe to the Inspector

Once the recipe is created, it can be passed to the Inspector object, which will then apply the recipe to the TargetShip objects, filtering out the trajectories that do not pass the rules.

from pytsa import Inspector

inspector = Inspector(all_ships, recipe)
accepted, rejected = inspector.inspect()

Visualizing AIS data

This module provides various functions for visualizing AIS data. Currently, there exist two groups of functions:

  • Functions for visualizing the empirical distribution functions used in the split-point approach in the original paper. These functions are located in the pytsa.visualization.ecdf module. They are intended to both make the results of the split-point approach more transparent and to provide a tool for adapting the split-point approach to different datasets.

  • Miscellaneous functions can be found in the pytsa.visualization.misc module. Currently, the following functionalities are provided:

    • Plotting the trajectories on a map
    • Comparing trajectories based on different σ_ssd ranges (see Figure 12 in the original paper)
    • Plotting all trajectories as a heatmap
    • Generating a pixel map of average smoothness as a function of the number of messages in a trajectory and the spatial standard deviation of the trajectory (Figure 14 in the paper)

Issues and Contributing

Currently, this project is developed by a single person and is therefore not thoroughly tested.

If you encounter any issues or have any suggestions for improvements, you are invited to open an issue or a pull request.

Citation

If you use this module in your research, please consider citing this repository as follows:

@misc{pytsa2024,
  author = {Paulig, Niklas},
  title = {{PyTSA}: Python Trajectory Splitting and Assessment Agent for AIS Data},
  year = {2024},
  publisher = {GitHub},
  journal = {GitHub repository},
  howpublished = {\url{https://github.com/nikpau/pytsa}},
}

Appendix

Split-point procedure

The split-point procedure takes place in the pytsa.tsea.split module. Its main function, is_split_point(), will be called on every pair of AIS messages in the dataset. The function returns a boolean value, indicating whether the pair of messages is a split point or not.

In case you want to adapt the split-point procedure to your dataset, you can use the pytsa.tsea.split module as a starting point.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

pytsa_ais-2.3.6-cp312-none-win_amd64.whl (1.3 MB view details)

Uploaded CPython 3.12 Windows x86-64

pytsa_ais-2.3.6-cp312-cp312-manylinux_2_34_x86_64.whl (3.2 MB view details)

Uploaded CPython 3.12 manylinux: glibc 2.34+ x86-64

pytsa_ais-2.3.6-cp312-cp312-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded CPython 3.12 macOS 11.0+ ARM64

pytsa_ais-2.3.6-cp311-none-win_amd64.whl (1.3 MB view details)

Uploaded CPython 3.11 Windows x86-64

pytsa_ais-2.3.6-cp311-cp311-manylinux_2_34_x86_64.whl (3.2 MB view details)

Uploaded CPython 3.11 manylinux: glibc 2.34+ x86-64

pytsa_ais-2.3.6-cp311-cp311-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded CPython 3.11 macOS 11.0+ ARM64

pytsa_ais-2.3.6-cp310-none-win_amd64.whl (1.3 MB view details)

Uploaded CPython 3.10 Windows x86-64

pytsa_ais-2.3.6-cp310-cp310-manylinux_2_34_x86_64.whl (3.2 MB view details)

Uploaded CPython 3.10 manylinux: glibc 2.34+ x86-64

pytsa_ais-2.3.6-cp310-cp310-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded CPython 3.10 macOS 11.0+ ARM64

pytsa_ais-2.3.6-cp39-none-win_amd64.whl (1.3 MB view details)

Uploaded CPython 3.9 Windows x86-64

pytsa_ais-2.3.6-cp39-cp39-manylinux_2_34_x86_64.whl (3.2 MB view details)

Uploaded CPython 3.9 manylinux: glibc 2.34+ x86-64

pytsa_ais-2.3.6-cp39-cp39-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded CPython 3.9 macOS 11.0+ ARM64

File details

Details for the file pytsa_ais-2.3.6-cp312-none-win_amd64.whl.

File metadata

  • Download URL: pytsa_ais-2.3.6-cp312-none-win_amd64.whl
  • Upload date:
  • Size: 1.3 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.0 CPython/3.12.4

File hashes

Hashes for pytsa_ais-2.3.6-cp312-none-win_amd64.whl
Algorithm Hash digest
SHA256 6943efbb79f9273328a6b610f243f22d454ab7aec72b4324adfc313324e61d47
MD5 f564308c5d215dc9b769cbbde07a00fb
BLAKE2b-256 89fa4fbc2ad2a061758eafbf0769a32de3314f8845179944c98de11f865bb90d

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp312-cp312-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for pytsa_ais-2.3.6-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 20a454568530de47f08151d7729b831358bb7467c086ed493b4212926b95856e
MD5 ffab12a69b3a84e380f66300a9b35a7c
BLAKE2b-256 8d974a20003f9a006537613381609f2ff8243985eceaae92359981f36a10631a

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pytsa_ais-2.3.6-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5365d09bf86299b7c2d53b5b30e42e99b1b9a6c7b0a957def4e1619c96b6cdd7
MD5 9430f7fe3945122126666c0be275a600
BLAKE2b-256 17585948ac13b572b2557add3961a75a67e4347a2c2ac445586944bd5ae3e2ba

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp311-none-win_amd64.whl.

File metadata

  • Download URL: pytsa_ais-2.3.6-cp311-none-win_amd64.whl
  • Upload date:
  • Size: 1.3 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.0 CPython/3.12.4

File hashes

Hashes for pytsa_ais-2.3.6-cp311-none-win_amd64.whl
Algorithm Hash digest
SHA256 3592eb48b2dade1ac8a33785a79f0e8116c42839bcec89fee8301366503d90e7
MD5 ac5faf6e4d007a9597daf8bdff8cb728
BLAKE2b-256 5f1948be6f2342c7b95960bffadefe52192a1c33e955e22a3823333f1f95f8db

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp311-cp311-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for pytsa_ais-2.3.6-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 442ee9945d93f7fb8b758dc9dbb8818956fb6597f35aa2a4c27a53c596c12e8a
MD5 6c0803c855f7eb05ee30f25de7728568
BLAKE2b-256 9a384b5d86522905c6ece8e5bd78258d91f37ef5166122511f8d597936e55e0d

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pytsa_ais-2.3.6-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d3721f468bc73ee68112597e6383894e18a2092ce64a01cb27d1ff01139db39a
MD5 e222fbbb08ef0b59904d1942ce51ea78
BLAKE2b-256 4861c374d350534e86d3151d0b280dbefa7a71cc45c7f8da5ab7130a97f45909

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp310-none-win_amd64.whl.

File metadata

  • Download URL: pytsa_ais-2.3.6-cp310-none-win_amd64.whl
  • Upload date:
  • Size: 1.3 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.0 CPython/3.12.4

File hashes

Hashes for pytsa_ais-2.3.6-cp310-none-win_amd64.whl
Algorithm Hash digest
SHA256 4452f293a19f062c3a8c2175830735013db9a5e08a20bfe96c58ade8d41226de
MD5 7da388bebcaf277b0654b4a7a5406c9e
BLAKE2b-256 44f83e3a27ab6ef05aea8bc6f45bc8fba63ad066980a93ffaf8ffcbfa6cc1a81

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp310-cp310-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for pytsa_ais-2.3.6-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 45b9ad551cb16e22d917cf2e4852f594f24941bd6e2d9bf6fc964f4126f0b3c7
MD5 8e5fe6685083e186778a1730fa470f0f
BLAKE2b-256 c1cef2ae371328f010f3a657f0804bd150d017323fc794c0da5a67a9b5cf2c80

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pytsa_ais-2.3.6-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 796d3a1cdb8956f0e4b2724b88a7e5cb94c6d0b200016b463873927e5b6c9fab
MD5 132befa223f1a8fa8e066e7038721ec1
BLAKE2b-256 0d39aefe60697c2af9dc0277116c33376bc0a646141a81192de893813848c540

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp39-none-win_amd64.whl.

File metadata

  • Download URL: pytsa_ais-2.3.6-cp39-none-win_amd64.whl
  • Upload date:
  • Size: 1.3 MB
  • Tags: CPython 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.0 CPython/3.12.4

File hashes

Hashes for pytsa_ais-2.3.6-cp39-none-win_amd64.whl
Algorithm Hash digest
SHA256 22286548bbb04b1790802d40db0528463ce297708cc684e36c053147f1fdf131
MD5 5b58a0ce08c228b54ee883d008ddfeb1
BLAKE2b-256 f8ef7513d6d7ada5ef52a6e59daa54811768ac7575f574066b96d01da7c10eab

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp39-cp39-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for pytsa_ais-2.3.6-cp39-cp39-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 f0cce4b7abde34a7e0409575eb68ba8d305f10307fa6d8e220ab0638ce0deb1f
MD5 cf217187113e403deec9f58282244797
BLAKE2b-256 abf36f390c980c75f2cb8af510058246018941910bec5e4a57da786940021452

See more details on using hashes here.

File details

Details for the file pytsa_ais-2.3.6-cp39-cp39-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pytsa_ais-2.3.6-cp39-cp39-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 fd7c7af9640fb5ab8dd84792f89e01b5fe4f0e648ba636b452a65631edaa0f66
MD5 4ca13ce7ff9ed69be5a029ccba181d3e
BLAKE2b-256 6833cfc3b54bd0ef68bff5c6cf5759ad5e5b7b555bd76a9a3f8023784cff277e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page