> ## Documentation Index
> Fetch the complete documentation index at: https://terminal49.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Vessel and Container Route Data

> Access detailed container route segments and vessel position data from the Terminal49 API to build richer map and shipment visibility experiences.

This is a technical article describing how to use the Routing Data feature, using the map as an example.

<Note>
  Routing Data (Container Map GeoJSON API) is a paid feature. These APIs are subject to additional terms of usage and pricing. See [Entitlements and Paid Features](/api-docs/useful-info/entitlements) for the required entitlement and non-entitled error response.
</Note>

## Table of contents

* [Overview](#overview)
* [Getting started](#getting-started)
* [Understanding the response](#understanding-the-response)
  * [GeoJSON FeatureCollection structure](#geojson-featurecollection-structure)
  * [Feature types](#feature-types)
    * [Port](#port)
    * [Current vessel](#current-vessel)
    * [Past vessel locations](#past-vessel-locations)
    * [Estimated full leg](#estimated-full-leg)
    * [Estimated partial leg](#estimated-partial-leg)
* [Building your map](#building-your-map)
* [Use cases](#use-cases)
* [Recommendations and best practices](#recommendations-and-best-practices)
* [Frequently asked questions](#frequently-asked-questions)

## Overview

The `GET /v2/containers/{id}/map_geojson` endpoint provides all the map-related data for a container in a single GeoJSON response.

The endpoint returns a GeoJSON FeatureCollection containing:

* **Port locations** (Point geometries): Port of lading (POL), port of discharge (POD), and transshipment ports (TS1, TS2, etc.)
* **Current vessel location** (Point geometry): The current position of the vessel if the container is currently at sea
* **Past vessel paths** (LineString geometries): Historical positions of vessels for completed and in-progress legs of the journey
* **Estimated future paths** (LineString geometries): Predicted vessel routes for upcoming or in-progress legs

<img src="https://mintcdn.com/terminal49/AqdbiLJIyX4Z6NxU/images/map_example.png?fit=max&auto=format&n=AqdbiLJIyX4Z6NxU&q=85&s=9d55ac33d06af7d3be5d33a4f16773ac" alt="Example of a shipment map" width="758" height="312" data-path="images/map_example.png" />

## Getting started

To retrieve the map data for a container, make a simple GET request to the endpoint:

```shell Request theme={null}
curl --request GET \
  --url https://api.terminal49.com/v2/containers/{id}/map_geojson \
  --header "Authorization: Token YOUR_API_KEY"
```

The response is a standard GeoJSON FeatureCollection that can be directly used with most mapping libraries (Leaflet, Mapbox GL, Google Maps, etc.).

<Accordion title="Example Response Overview">
  ```json theme={null}
  {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "geometry": {
          "type": "Point",
          "coordinates": [
            100.896831042,
            13.065302386
          ]
        },
        "properties": {
          "feature_type": "port",
          "ports_sequence": 1,
          "ports_total": 3,
          "label": "POL",
          "name": "Laem Chabang",
          // ... more properties
        }
      },
      {
        "type": "Feature",
        "geometry": {
          "type": "LineString",
          "coordinates": [
            [
              100.868768333,
              13.07306
            ],
            [
              100.839155,
              13.079318333
            ],
            // ... more coordinates
            [
              118.03862,
              24.440998333
            ]
          ]
        },
        "properties": {
          "feature_type": "past_vessel_locations",
          "vessel_id": "87a12f43-766c-4078-89bc-ac6595082f7b",
          // ... more path properties
        }
      },
      // ... more features
    ]
  }
  ```
</Accordion>

## Understanding the response

### GeoJSON FeatureCollection structure

The response follows the [GeoJSON specification](https://geojson.org/) and contains:

* `type`: Always `"FeatureCollection"`
* `features`: An array of GeoJSON Feature objects, each representing a map element (port, vessel, or route path)

Each feature contains:

* `type`: Always `"Feature"`
* `geometry`: A GeoJSON geometry object (Point or LineString)
* `properties`: An object containing metadata specific to the feature type

### Feature types

The `properties.feature_type` field identifies what each feature represents. The following feature types are available:

#### Port

Geometry Type: `Point`

Port features represent all ports in the container's route: the port of lading (POL), port of discharge (POD), and any transshipment ports (TS1, TS2, etc.).

<Accordion title="Port Feature Example">
  ```json theme={null}
  {
    "type": "Feature",
    "geometry": {
      "type": "Point",
      "coordinates": [
        100.896831042,
        13.065302386
      ]
    },
    "properties": {
      "feature_type": "port",
      "ports_sequence": 1,
      "ports_total": 3,
      "location_id": "c5adae24-6fd4-4720-8813-976cf206feb1",
      "location_type": "Port",
      "name": "Laem Chabang",
      "state_abbr": "20",
      "state": null,
      "country_code": "TH",
      "country": "Thailand",
      "time_zone": "Asia/Bangkok",
      "inbound_eta_at": null,
      "inbound_ata_at": null,
      "outbound_etd_at": null,
      "outbound_atd_at": "2025-11-08T00:44:52Z",
      "label": "POL",
      "updated_at": "2025-12-11T09:01:08Z"
    }
  }
  ```
</Accordion>

<Accordion title="Port Feature Properties">
  | Property          | Type           | Description                                                         |
  | ----------------- | -------------- | ------------------------------------------------------------------- |
  | `feature_type`    | string         | Always `"port"`                                                     |
  | `ports_sequence`  | integer        | The sequence number of this port in the route (1 = POL, last = POD) |
  | `ports_total`     | integer        | Total number of ports in the route                                  |
  | `location_id`     | string         | Unique identifier for the port location                             |
  | `location_type`   | string         | Always `"Port"`                                                     |
  | `name`            | string         | Name of the port                                                    |
  | `state_abbr`      | string \| null | State abbreviation (if applicable)                                  |
  | `state`           | string \| null | State name (if applicable)                                          |
  | `country_code`    | string         | ISO country code                                                    |
  | `country`         | string         | Country name                                                        |
  | `time_zone`       | string         | IANA timezone identifier                                            |
  | `label`           | string         | Port label: `"POL"`, `"POD"`, or `"TS1"`, `"TS2"`, etc.             |
  | `inbound_eta_at`  | string \| null | Estimated time of arrival (ISO 8601)                                |
  | `inbound_ata_at`  | string \| null | Actual time of arrival (ISO 8601)                                   |
  | `outbound_etd_at` | string \| null | Estimated time of departure (ISO 8601)                              |
  | `outbound_atd_at` | string \| null | Actual time of departure (ISO 8601)                                 |
  | `updated_at`      | string \| null | Last update timestamp from the shipment (ISO 8601)                  |
</Accordion>

#### Current vessel

Geometry Type: `Point`

This feature is only present when the container is currently on a vessel at sea. It represents the vessel's current position.

<Accordion title="Current Vessel Feature Example">
  ```json theme={null}
  {
    "type": "Feature",
    "geometry": {
      "type": "Point",
      "coordinates": [
        -131.128473333,
        31.023033333
      ]
    },
    "properties": {
      "feature_type": "current_vessel",
      "ports_sequence": 2,
      "vessel_id": "93fc5dce-4c7f-4089-bd28-f20cd9202ab0",
      "vessel_name": "ZIM BANGKOK",
      "vessel_imo": "9936525",
      "voyage_number": "13E",
      "vessel_location_timestamp": "2025-12-11T11:46:03Z",
      "vessel_location_heading": 108,
      "vessel_location_speed": 21,
      "departure_port_id": "ed64d446-9098-420c-ab08-c127e62509fe",
      "departure_port_name": "Xiamen",
      "departure_port_state_abbr": "FJ",
      "departure_port_state": null,
      "departure_port_country_code": "CN",
      "departure_port_country": "China",
      "departure_port_label": "TS1",
      "departure_port_atd": "2025-11-19T16:00:00Z",
      "departure_port_time_zone": "Asia/Shanghai",
      "arrival_port_id": "6129528d-846e-4571-ae16-b5328a4285ab",
      "arrival_port_name": "Savannah",
      "arrival_port_state_abbr": "GA",
      "arrival_port_state": "Georgia",
      "arrival_port_country_code": "US",
      "arrival_port_country": "United States",
      "arrival_port_label": "POD",
      "arrival_port_eta": "2025-12-31T05:00:00Z",
      "arrival_port_time_zone": "America/New_York"
    }
  }
  ```
</Accordion>

<Accordion title="Current Vessel Feature Properties">
  | Property                      | Type           | Description                                           |
  | ----------------------------- | -------------- | ----------------------------------------------------- |
  | `feature_type`                | string         | Always `"current_vessel"`                             |
  | `ports_sequence`              | integer        | Sequence number of the departure port for this leg    |
  | `vessel_id`                   | string         | Unique identifier for the vessel                      |
  | `vessel_name`                 | string         | Name of the vessel                                    |
  | `vessel_imo`                  | string         | IMO number of the vessel                              |
  | `voyage_number`               | string \| null | Voyage number for this leg                            |
  | `vessel_location_timestamp`   | string         | Timestamp of the vessel position (ISO 8601)           |
  | `vessel_location_heading`     | number \| null | Vessel heading in degrees (0-360)                     |
  | `vessel_location_speed`       | number \| null | Vessel speed in knots                                 |
  | `departure_port_id`           | string         | ID of the port the vessel departed from               |
  | `departure_port_name`         | string         | Name of the departure port                            |
  | `departure_port_state_abbr`   | string \| null | State abbreviation of departure port                  |
  | `departure_port_state`        | string \| null | State name of departure port                          |
  | `departure_port_country_code` | string         | Country code of departure port                        |
  | `departure_port_country`      | string         | Country name of departure port                        |
  | `departure_port_label`        | string         | Label of departure port (POL, POD, TS1, etc.)         |
  | `departure_port_atd`          | string \| null | Actual time of departure from the port (ISO 8601)     |
  | `departure_port_time_zone`    | string         | Timezone of departure port                            |
  | `arrival_port_id`             | string \| null | ID of the next port the vessel is heading to          |
  | `arrival_port_name`           | string \| null | Name of the arrival port                              |
  | `arrival_port_state_abbr`     | string \| null | State abbreviation of arrival port                    |
  | `arrival_port_state`          | string \| null | State name of arrival port                            |
  | `arrival_port_country_code`   | string \| null | Country code of arrival port                          |
  | `arrival_port_country`        | string \| null | Country name of arrival port                          |
  | `arrival_port_label`          | string \| null | Label of arrival port (POL, POD, TS1, etc.)           |
  | `arrival_port_eta`            | string \| null | Estimated time of arrival at the next port (ISO 8601) |
  | `arrival_port_time_zone`      | string \| null | Timezone of arrival port                              |
</Accordion>

#### Past vessel locations

Geometry Type: `LineString`

These features represent the actual historical paths taken by vessels for completed and in-progress legs of the journey. Each LineString contains a series of coordinates showing where the vessel traveled between two ports.

<Accordion title="Past Vessel Locations Feature Example">
  ```json theme={null}
  {
    "type": "Feature",
    "geometry": {
      "type": "LineString",
      "coordinates": [
        [
          100.868768333,
          13.07306
        ],
        [
          100.839155,
          13.079318333
        ],
        // ... many more coordinates
        [
          118.03862,
          24.440998333
        ]
      ]
    },
    "properties": {
      "feature_type": "past_vessel_locations",
      "ports_sequence": 1,
      "vessel_id": "87a12f43-766c-4078-89bc-ac6595082f7b",
      "start_time": "2025-11-08T00:44:52Z",
      "end_time": "2025-11-15T16:00:00Z",
      "point_count": 546,
      "outbound_atd_at": "2025-11-08T00:44:52Z",
      "inbound_ata_at": "2025-11-15T16:00:00Z",
      "inbound_eta_at": null
    }
  }
  ```
</Accordion>

<Accordion title="Past Vessel Locations Properties">
  | Property          | Type           | Description                                                  |
  | ----------------- | -------------- | ------------------------------------------------------------ |
  | `feature_type`    | string         | Always `"past_vessel_locations"`                             |
  | `ports_sequence`  | integer        | Sequence number of the departure port for this leg           |
  | `vessel_id`       | string         | Unique identifier for the vessel that traveled this path     |
  | `start_time`      | string         | Start timestamp of the path (ISO 8601)                       |
  | `end_time`        | string         | End timestamp of the path (ISO 8601)                         |
  | `point_count`     | integer        | Number of coordinate points in the LineString                |
  | `outbound_atd_at` | string \| null | Actual time of departure from the origin port (ISO 8601)     |
  | `inbound_ata_at`  | string \| null | Actual time of arrival at the destination port (ISO 8601)    |
  | `inbound_eta_at`  | string \| null | Estimated time of arrival at the destination port (ISO 8601) |
</Accordion>

#### Estimated full leg

Geometry Type: `LineString`

These features represent predicted vessel paths for future legs that have not yet started. The LineString shows the estimated route between two ports.

<Accordion title="Estimated Full Leg Feature Example">
  ```json theme={null}
  {
    "type": "Feature",
    "geometry": {
      "type": "LineString",
      "coordinates": [
        [55.059917502, 24.987353081],
        [55.234, 24.856],
        [56.123, 24.567],
        // ... intermediate estimated points
        [79.851136851, 6.942742853]
      ]
    },
    "properties": {
      "feature_type": "estimated_full_legs",
      "ports_sequence": 2,
      "previous_port_id": "94892d07-ef8f-4f76-a860-97a398c2c177",
      "next_port_id": "818ef299-aed3-49c9-b3f7-7ee205f697f6",
      "point_count": 87
    }
  }
  ```
</Accordion>

<Accordion title="Estimated Full Leg Properties">
  | Property           | Type    | Description                                        |
  | ------------------ | ------- | -------------------------------------------------- |
  | `feature_type`     | string  | Always `"estimated_full_legs"`                     |
  | `ports_sequence`   | integer | Sequence number of the departure port for this leg |
  | `previous_port_id` | string  | ID of the origin port                              |
  | `next_port_id`     | string  | ID of the destination port                         |
  | `point_count`      | integer | Number of coordinate points in the LineString      |
</Accordion>

#### Estimated partial leg

Geometry Type: `LineString`

This feature represents the predicted path from the vessel's current position to the next port. It is only present when the container is currently on a vessel at sea.

<Accordion title="Estimated Partial Leg Feature Example">
  ```json theme={null}
  {
    "type": "Feature",
    "geometry": {
      "type": "LineString",
      "coordinates": [
        [
          -131.128473333,
          31.023033333
        ],
        [
          -130.9177,
          30.67224
        ],
        // ... many more coordinates
        [
          -80.91232,
          32.03728
        ]
      ]
    },
    "properties": {
      "feature_type": "estimated_partial_leg",
      "ports_sequence": 2,
      "current_port_id": "ed64d446-9098-420c-ab08-c127e62509fe",
      "next_port_id": "6129528d-846e-4571-ae16-b5328a4285ab",
      "point_count": 364
    }
  }
  ```
</Accordion>

<Accordion title="Estimated Partial Leg Properties">
  | Property          | Type    | Description                                        |
  | ----------------- | ------- | -------------------------------------------------- |
  | `feature_type`    | string  | Always `"estimated_partial_leg"`                   |
  | `ports_sequence`  | integer | Sequence number of the departure port for this leg |
  | `current_port_id` | string  | ID of the port the vessel departed from            |
  | `next_port_id`    | string  | ID of the next port the vessel is heading to       |
  | `point_count`     | integer | Number of coordinate points in the LineString      |
</Accordion>

<Note>
  This feature is only present when there is a `current_vessel` feature. The LineString starts from the vessel's current position (which matches the `current_vessel` feature coordinates) and extends to the next port.
</Note>

## Building your map

To visualize a container's journey using the GeoJSON response on your own map (similar to [the embeddable map](/api-docs/in-depth-guides/terminal49-map)):

1. **Load the GeoJSON data** into your mapping library (Leaflet, Mapbox GL, Google Maps, etc.)

2. **Filter features by type** to style them differently:
   * **Ports**: Display as markers with labels (POL, POD, TS1, etc.)
   * **Current vessel**: Display as a special marker (e.g., a ship icon) with vessel information
   * **Past vessel locations**: Display as solid lines (representing completed journeys)
   * **Estimated partial leg** and **Estimated full legs**: Display as dashed lines (representing future predictions)

3. **Use the properties** to add interactivity:
   * Show port details (name, country, timestamps) on click/hover
   * Display vessel information (name, IMO, speed, heading) for the current vessel
   * Show leg information (departure/arrival times, vessel ID) for path segments

<Accordion title="Example: Using Leaflet.js">
  ```javascript theme={null}
  // Fetch the GeoJSON data
  fetch('https://api.terminal49.com/v2/containers/{id}/map_geojson', {
    headers: {
      'Authorization': 'Token YOUR_API_KEY'
    }
  })
  .then(response => response.json())
  .then(geojson => {
    // Create a map
    const map = L.map('map').setView([20, 70], 3);
    
    // Add base layer
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
    
    // Process each feature
    geojson.features.forEach(feature => {
      const props = feature.properties;
      
      if (props.feature_type === 'port') {
        // Add port marker
        const marker = L.marker([feature.geometry.coordinates[1], feature.geometry.coordinates[0]])
          .addTo(map)
          .bindPopup(`<b>${props.label}</b><br>${props.name}`);
      } else if (props.feature_type === 'current_vessel') {
        // Add current vessel marker
        const vesselIcon = L.icon({
          iconUrl: 'vessel-icon.png',
          iconSize: [32, 32]
        });
        L.marker([feature.geometry.coordinates[1], feature.geometry.coordinates[0]], {icon: vesselIcon})
          .addTo(map)
          .bindPopup(`<b>${props.vessel_name}</b><br>Speed: ${props.vessel_location_speed} knots`);
      } else if (props.feature_type === 'past_vessel_locations') {
        // Add past path as solid line
        const coordinates = feature.geometry.coordinates.map(coord => [coord[1], coord[0]]);
        L.polyline(coordinates, {color: 'green', weight: 3})
          .addTo(map);
      } else if (props.feature_type === 'estimated_full_legs' || props.feature_type === 'estimated_partial_leg') {
        // Add estimated path as dashed line
        const coordinates = feature.geometry.coordinates.map(coord => [coord[1], coord[0]]);
        L.polyline(coordinates, {color: 'blue', weight: 2, dashArray: '10, 10'})
          .addTo(map);
      }
    });
  });
  ```
</Accordion>

## Use cases

Integrating Terminal49's Vessel and Container Route APIs enables a variety of advanced capabilities:

* **Track Complete Shipment Journeys Visually:** Monitor shipments across multiple legs on a map, from the port of lading to the port of discharge, including all transshipment points.
* **Identify Transshipment Details Geographically:** Clearly see where transshipments occur and the routes taken between them.
* **Correlate Timestamps with Locations:** Visually connect ETDs, ETAs, ATDs, and ATAs for every leg with their geographical points on the map for precise planning and exception management.
* **Improve Internal Logistics Dashboards:** Offer your operations team a clear visual overview of all ongoing shipments and their current locations.

## Recommendations and best practices

* **Polling Intervals**: For active containers (currently at sea), refresh the map data up to once per hour to get updated vessel positions. For containers that have completed their journey, you can cache the data as it won't change.

* **Error Handling**: Implement proper error handling for API requests. If a container doesn't have route data yet, the endpoint will return an empty FeatureCollection (`{"type": "FeatureCollection", "features": []}`).

If you decide to create your own map:

* **Data Layering:** Consider layering information on your map. Start with basic port markers and paths, then add details like vessel names, ETAs, or status on hover or click.

* **Map Library Integration:** Use a robust mapping library (e.g., Leaflet, Mapbox GL, Google Maps, OpenLayers) to handle the rendering of markers, lines, and map interactivity.

* **Styling Guidelines**:
  * Use distinct colors/styles for different feature types (ports, current vessel, past paths, estimated paths)
  * Consider using dashed lines for estimated paths and solid lines for completed paths
  * Add labels to port markers showing POL, POD, TS1, etc.
  * Display vessel information in popups or info panels

* **Data Interpretation**:
  * The `ports_sequence` property helps you understand the order of ports in the journey
  * Use `inbound_ata_at` and `outbound_atd_at` to determine which legs are completed
  * The presence of a `current_vessel` feature indicates the container is currently at sea

* **Handling Antimeridian Crossings**: When container routes cross the International Date Line (antimeridian at ±180° longitude), standard map projections can display routes incorrectly, showing lines that wrap around the entire globe. For mapping libraries that don't natively handle antimeridian crossings, the recommended approach is to: (1) detect and split crossing LineStrings into separate segments, and (2) render map features across multiple world views (standard, East, and West) as needed. Below are more details:

  * **Detection**: Identify LineString features (past vessel locations or estimated paths) that cross the antimeridian by checking if consecutive coordinates have a longitude difference greater than 180°.

  * **Single Crossing Solution**: When one antimeridian crossing is detected:
    * Split the route into two segments: features before the crossing (based on `ports_sequence`) are drawn in the standard world view
    * Features after the crossing are drawn in an extended world view (East or West, depending on crossing direction)
    * Split the crossing LineString into two separate lines: one ending at the antimeridian in the standard view, and one starting from the antimeridian in the extended view

  * **Multiple Crossings**: For routes with more than one antimeridian crossing (rare but possible), render all features across three world views (standard, East, and West) with duplicated features. Split all crossing lines to prevent lines from wrapping across the globe.

  * **No Crossings**: If no antimeridian crossings are detected, render all features in the standard world view without any special handling.

## Frequently asked questions

<AccordionGroup>
  <Accordion title="How up-to-date is the vessel position data?">
    Vessel location data is updated every 15 minutes, although that does not guarantee there will be a new position every 15 minutes due to factors like whether the vessel is transmitting or within range of a satellite or base station.
  </Accordion>

  <Accordion title="How accurate are the future predictions?">
    Predicted future positions are based on algorithms and historical data. Their accuracy can vary based on many factors such as temporary deviations, weather conditions, seasonality, or how frequently the shipping lane is used.
  </Accordion>

  <Accordion title="What if a vessel deviates from the predicted path?">
    Predicted paths are estimates. The historical path (once available as a `past_vessel_locations` feature) will show the actual route taken. Regularly refreshing data for active shipments is key to getting the most accurate information.
  </Accordion>

  <Accordion title="Why don't I see a current_vessel feature for my container?">
    The `current_vessel` feature is only present when:

    * The container is currently on a vessel at sea
    * The vessel has departed from a port (`outbound_atd_at` is present)
    * The next port hasn't been reached yet (`inbound_ata_at` is not present)
    * A valid vessel location can be retrieved
  </Accordion>

  <Accordion title="Can I get map data for multiple containers at once?">
    Currently, the endpoint returns data for a single container. You'll need to make separate API calls for each container you want to display on your map.
  </Accordion>

  <Accordion title="What coordinate system is used?">
    All coordinates follow the GeoJSON standard: `[longitude, latitude]` in WGS84 (EPSG:4326) format.
  </Accordion>

  <Accordion title="Are the LineString coordinates simplified?">
    The endpoint applies simplification to reduce the number of points in LineStrings for better performance. The simplification tolerance can vary, but the paths remain accurate for visualization purposes.
  </Accordion>
</AccordionGroup>
