Open In Colab

Analyze a fleet in Ghanaian EEZ

This guide provides detailed instructions to on how to use the gfw-api-python-client to Monitor a Fleet (a group of vessels) of Tuna Longliners in Ghanaian EEZ region for Compliance using 4Wings API, Vessels API, and Events API.

Note: See the Datasets, Data Caveats, and Terms of Use pages in the GFW API documentation for details on GFW data, API licenses, and rate limits.

Prerequisites

Before using the gfw-api-python-client, ensure it is installed (see the Getting Started guide) and that you have obtained an API access token from the Global Fishing Watch API portal.

Installation

The gfw-api-python-client can be easily installed using pip:

# %pip install gfw-api-python-client

Usage

Import and use gfw-api-python-client in your Python codes

import os

import pandas as pd

import gfwapiclient as gfw
try:
    from google.colab import userdata

    access_token = userdata.get("GFW_API_ACCESS_TOKEN")
except Exception:
    access_token = os.environ.get("GFW_API_ACCESS_TOKEN")

access_token = access_token or "<PASTE_YOUR_GFW_API_ACCESS_TOKEN_HERE>"
gfw_client = gfw.Client(
    access_token=access_token,
)

Introduction

Use Case: Monitoring a Fleet of Tuna Longliners for Compliance

Kwame is a fisheries compliance officer in Ghana, responsible for monitoring a fleet of tuna longliners operating within Ghanaian Exclusive Economic Zone (EEZ). His goal is to:

  1. Track apparent fishing effort for longliners over the last 12 months.

  2. Identify potential vessels in this fleet, their operational patterns, and their activity levels.

  3. Retrieve vessel details, including flag state, ownership history, and authorizations.

  4. Analyze events such as port visits and encounters (potential transshipment) activities.

APIs Used:

  1. 4Wings API – Retrieve apparent fishing effort grouped by vessel ID in Ghanaian EEZ.

  2. Vessels API – Get vessel identity, ownership, and compliance details.

  3. Events API – Identify port visits and potential transshipment activities for vessels in the fleet.

Important: In order to avoid any misinterpretation of GFW data, please refer to our official data caveats documentations:

Important Caveats:

  1. The 4Wings API only supports one active report per user at a time.

  2. Sending multiple requests simultaneously results in a 429 Too Many Requests error.

  3. If a report takes over 100 seconds to generate, it may return a 524 Gateway Timeout error.

Step 0: Identify the Region of Interest (ROI) - Ghanaian EEZ

Before making API requests, Kwame must specify the geographic area for analysis using a Region ID:

Options to Define the Region:

  1. Using Region ID - Each EEZ has a unique ID in the public-eez-areas dataset.

  2. Custom Geometries - Users can define a custom area using GeoJSON.

For Ghanaian EEZ, the region ID is 8400 (public-eez-areas dataset).

Step 1: Retrieve Fishing Effort in Ghanaian EEZ

Kwame first queries the 4Wings API to get fishing effort for all vessels, grouping them by gear type in Ghanaian EEZ. Please learn more about apparent fishing effort here and check its data caveats here.

Filters Used:

  1. Region ID - 8400 Ghanaian EEZ

  2. Date Range - Last 12 Months

  3. Grouped By - Gear Type

step_1_report_result = await gfw_client.fourwings.create_fishing_effort_report(
    spatial_resolution="LOW",
    group_by="GEARTYPE",
    temporal_resolution="ENTIRE",
    start_date="2024-01-01",
    end_date="2025-01-01",
    spatial_aggregation=True,
    region={
        "dataset": "public-eez-areas",
        "id": "8400",
    },
)
step_1_report_df = step_1_report_result.df()
step_1_report_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9 entries, 0 to 8
Data columns (total 20 columns):
 #   Column                   Non-Null Count  Dtype  
---  ------                   --------------  -----  
 0   date                     9 non-null      object 
 1   detections               0 non-null      object 
 2   flag                     0 non-null      object 
 3   gear_type                9 non-null      object 
 4   hours                    9 non-null      float64
 5   vessel_ids               9 non-null      int64  
 6   vessel_id                0 non-null      object 
 7   vessel_type              0 non-null      object 
 8   entry_timestamp          0 non-null      object 
 9   exit_timestamp           0 non-null      object 
 10  first_transmission_date  0 non-null      object 
 11  last_transmission_date   0 non-null      object 
 12  imo                      0 non-null      object 
 13  mmsi                     0 non-null      object 
 14  call_sign                0 non-null      object 
 15  dataset                  0 non-null      object 
 16  report_dataset           9 non-null      object 
 17  ship_name                0 non-null      object 
 18  lat                      0 non-null      object 
 19  lon                      0 non-null      object 
dtypes: float64(1), int64(1), object(18)
memory usage: 1.5+ KB
step_1_report_df[["flag", "gear_type", "hours", "vessel_ids"]].head()
flag gear_type hours vessel_ids
0 None purse_seines 6.340556 1
1 None drifting_longlines 593.138333 3
2 None other_purse_seines 26.581111 1
3 None fishing 20929.563333 21
4 None inconclusive 30496.173611 27
step_1_agg_report_df = (
    step_1_report_df.groupby(["gear_type"], as_index=False)
    .agg(hours=("hours", "sum"), vessel_ids=("vessel_ids", "sum"))
    .sort_values(by="hours", ascending=False)
)
step_1_agg_report_df
gear_type hours vessel_ids
7 trawlers 46704.797222 26
3 inconclusive 30496.173611 27
1 fishing 20929.563333 21
8 tuna_purse_seines 5146.623889 23
5 pole_and_line 3481.509167 5
0 drifting_longlines 593.138333 3
4 other_purse_seines 26.581111 1
6 purse_seines 6.340556 1
2 fixed_gear 0.163611 1

What We have Learned from Step 1

  1. Kwame now has apparent fishing effort data for multiple gear types.

  2. There are potential 3 vessels operating as a longliners (i.e., drifting_longlines) in Ghanaian EEZ with 593.138333 hours logged.

Step 2: Retrieve Vessel IDs for Longliners

Kwame refines his 4Wings API request to group by vessel ID, and filtering only for longliners in Ghanaian EEZ.

Filters Used:

  1. Region ID - 8400 Ghanaian EEZ

  2. Date Range - Last 12 Months

  3. Grouped By - Vessel ID

Why Use group-by=VESSEL_ID?

Grouping by VESSEL_ID allows individual vessel identification in the response. This is crucial for tracking vessel activity and, more importantly, linking each detected vessel to the Vessels API in the next step. By structuring the query this way, we can fetch vessel details such as flag, name, and ownership records in Step 3 below.

step_2_report_result = await gfw_client.fourwings.create_fishing_effort_report(
    spatial_resolution="LOW",
    group_by="VESSEL_ID",
    temporal_resolution="ENTIRE",
    filters=["geartype in ('drifting_longlines')"],
    start_date="2024-01-01",
    end_date="2025-01-01",
    spatial_aggregation=True,
    region={
        "dataset": "public-eez-areas",
        "id": "8400",
    },
)
step_2_report_df = step_2_report_result.df()
step_2_report_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 20 columns):
 #   Column                   Non-Null Count  Dtype              
---  ------                   --------------  -----              
 0   date                     3 non-null      object             
 1   detections               0 non-null      object             
 2   flag                     3 non-null      object             
 3   gear_type                3 non-null      object             
 4   hours                    3 non-null      float64            
 5   vessel_ids               0 non-null      object             
 6   vessel_id                3 non-null      object             
 7   vessel_type              3 non-null      object             
 8   entry_timestamp          3 non-null      datetime64[ns, UTC]
 9   exit_timestamp           3 non-null      datetime64[ns, UTC]
 10  first_transmission_date  3 non-null      datetime64[ns, UTC]
 11  last_transmission_date   3 non-null      datetime64[ns, UTC]
 12  imo                      3 non-null      object             
 13  mmsi                     3 non-null      object             
 14  call_sign                3 non-null      object             
 15  dataset                  3 non-null      object             
 16  report_dataset           3 non-null      object             
 17  ship_name                3 non-null      object             
 18  lat                      0 non-null      object             
 19  lon                      0 non-null      object             
dtypes: datetime64[ns, UTC](4), float64(1), object(15)
memory usage: 612.0+ bytes
step_2_report_df[["flag", "gear_type", "hours", "mmsi", "ship_name"]].head()
flag gear_type hours mmsi ship_name
0 CHN DRIFTING_LONGLINES 2.750556 412331032
1 JPN DRIFTING_LONGLINES 588.879722 431100690 SENSHU MARU NO.3
2 TWN DRIFTING_LONGLINES 1.508056 416007496 HUNG CHUAN SHUN

What We have Learned from Step 2

  1. Kwame identifies 3 vessels operating as longliners within Ghanaian EEZ.

  2. The vessel (mmsi: 431100690, ship_name: SENSHU MARU NO.3) shows significant activity with 588.879722 hours logged.

  3. Other vessels (mmsi: 416007496, ship_name: HUNG CHUAN SHUN) and (mmsi: 412331032) shows apparent fishing effort over a short duration.

  4. This response is based on AIS self-reported data and should be further validated.

Step 3: Retrieve Vessel Details Using the Vessels API

Kwame queries the Vessels API to get detailed vessel identity and ownership records. Please learn more about Vessels API here and check its data caveats here.

Filters Used:

  1. Vessel IDs from 4Wings API, Step 2 above.

  2. Datasets - public-global-vessel-identity:latest.

  3. Includes - POTENTIAL_RELATED_SELF_REPORTED_INFO.

Note: Vessels may change identifiers over time, such as their Maritime Mobile Service Identity (MMSI), International Maritime Organization (IMO) number), call sign, or even their name. These changes can occur due to re-registration, changes in ownership, or other operational reasons within the AIS transponder. Parameter (includes = POTENTIAL_RELATED_SELF_REPORTED_INFO) helps group all vessel ids that are potentially related as part of the same physical vessel based on publicly available registry information.

step_2_vessel_ids = list(step_2_report_df["vessel_id"].unique())
step_2_vessel_ids
['f37ebdc1b-be44-0740-7904-49397360e29d',
 'b1dad8628-8c9c-2ee7-258b-3d8fb747f1c8',
 '60f7bb972-2c90-4553-650b-23c38f9521bf']
step_3_vessels_result = await gfw_client.vessels.get_vessels_by_ids(
    ids=step_2_vessel_ids,
)
step_3_vessels_df = step_3_vessels_result.df()
step_3_vessels_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 7 columns):
 #   Column                          Non-Null Count  Dtype 
---  ------                          --------------  ----- 
 0   dataset                         3 non-null      object
 1   registry_info_total_records     3 non-null      int64 
 2   registry_info                   3 non-null      object
 3   registry_owners                 3 non-null      object
 4   registry_public_authorizations  3 non-null      object
 5   combined_sources_info           3 non-null      object
 6   self_reported_info              3 non-null      object
dtypes: int64(1), object(6)
memory usage: 300.0+ bytes

Understanding Vessel Details Response Data

  • registryInfoTotalRecords – This represents the number of registry records found for the vessels.

  • registryInfo – Contains public registry data. This data is sourced from official vessel registries.

  • registryOwners – Lists the registered owners of the vessel based on public sources.

  • registryPublicAuthorizations – Represents known fishing authorizations from public sources. Users should verify against national registries and RFMO records for additional context.

  • combinedSourcesInfo – Provides inferred data from multiple sources, including. This is not explicitly reported by vessels but determined through GFW’s classification methods.

  • selfReportedInfo – Contains AIS self-reported data, including MMSI, ship name, and flag as broadcast by the vessel itself. Self-reported data may not always align with registry data and should be cross-checked.

step_3_vessels_df[["registry_info", "registry_owners", "self_reported_info"]]
registry_info registry_owners self_reported_info
0 [{'id': '02eda7d2da02943eecd48813fb7d562a', 's... [{'name': 'HER RONG SHUN FISHERIES', 'flag': '... [{'id': '60f7bb972-2c90-4553-650b-23c38f9521bf...
1 [{'id': '0b0dec977c1aad4ae6652c4076572cc7', 's... [{'name': 'TOMIOKA FISHERIES', 'flag': 'JPN', ... [{'id': 'c86d138c5-5d51-3620-fe01-a9e0ed37fb91...
2 [] [] [{'id': 'f37ebdc1b-be44-0740-7904-49397360e29d...

Explore Vessels Registry Info

step_3_registry_info_df = pd.json_normalize(
    step_3_vessels_df["registry_info"].explode()
)
step_3_registry_info_df = step_3_registry_info_df.dropna()
step_3_registry_info_df[
    ["ssvid", "flag", "ship_name", "n_ship_name", "gear_types", "source_code"]
]
ssvid flag ship_name n_ship_name gear_types source_code
0 416007496 TWN HUNG CHUAN SHUN HUNGCHUANSHUN [DRIFTING_LONGLINES] [ICCAT, IMO, ISSF, OPRT, TMT_ICCAT, TMT_OTHER_...
1 431100690 JPN SENSHU MARU NO.3 SENSHUMARU3 [DRIFTING_LONGLINES] [CCSBT, GFCM, IATTC, ICCAT, IMO, IOTC, OPRT, R...

Explore Registry Owners

step_3_registry_owners_df = pd.json_normalize(
    step_3_vessels_df["registry_owners"].explode()
)
step_3_registry_owners_df = step_3_registry_owners_df.dropna()
step_3_registry_owners_df[["ssvid", "flag", "name", "source_code"]]
ssvid flag name source_code
0 416007496 TWN HER RONG SHUN FISHERIES [TMT_ICCAT, TMT_OTHER_OFFICIAL]
1 431100690 JPN TOMIOKA FISHERIES [TMT_CCSBT, TMT_IATTC, TMT_ICCAT, TMT_OTHER_OF...
2 431100690 JPN TOMIOKA [TMT_CCSBT, TMT_IATTC, TMT_ICCAT, TMT_OTHER_OF...
3 431100690 JPN YAMAMOTO YUUKI [TMT_CCSBT, TMT_IATTC, TMT_ICCAT, TMT_OTHER_OF...
4 431100690 JPN YAMAMOTO HIROKI [TMT_CCSBT, TMT_IATTC, TMT_ICCAT, TMT_OTHER_OF...

Explore Vessels Self Reported Info

step_3_self_reported_info_df = pd.json_normalize(
    step_3_vessels_df["self_reported_info"].explode()
)
step_3_self_reported_info_df[
    ["ssvid", "flag", "ship_name", "n_ship_name", "source_code"]
]
ssvid flag ship_name n_ship_name source_code
0 416007496 TWN HUNG CHUAN SHUN HUNGCHUANSHUN [AIS]
1 431100690 JPN None None [AIS]
2 431100690 JPN SENSHU MARU NO.3 SENSHUMARU3 [AIS]
3 431100690 JPN SENSHU MARU NO3 SENSHUMARU3 [AIS]
4 412331032 CHN None None [AIS]

What We have Learned from Step 3

  • The vessel mmsi/ssvid: 412331032 appears to be a drifting longliner flagged under China.

  • No public registry data is found for this vessel.

  • The vessel’s identity information is based on AIS self-reported data, which may not always align with official registries.

  • The vessel appears to have been active since 2014, based on self-reported AIS records.

  • This vessel’s data needs further validation against official public sources

Step 4: Detect Fleet Activity (Port Visits)

Now that Kwame has identified vessels in the fleet, he examines their activity further by querying the Events API. This allows him to detect port visits, encounters (Potential Transshipment) and apparent fishing activity based on vessel movement patterns. Please learn more about Events API here and check its data caveats here.

Filters Used:

  1. Vessel ID from 4Wings API

  2. Event Types - Port visits, encounters (potential transshipment), and fishing events.

  3. Time Range - Last 6 months.

  4. Datasets:

    • public-global-port-visits-events::latest (Port Visits)

    • public-global-encounters-events:latest (Encounters between vessels)

    • public-global-fishing-events:latest (Fishing activity)

  5. Encounter Types - FISHING-FISHING

step_4_events_result = await gfw_client.events.get_all_events(
    datasets=[
        "public-global-encounters-events:latest",
        "public-global-fishing-events:latest",
        "public-global-port-visits-events:latest",
    ],
    vessels=step_2_vessel_ids,
    types=["ENCOUNTER", "FISHING", "PORT_VISIT"],
    start_date="2024-08-01",
    end_date="2025-01-31",
    encounter_types=["FISHING-FISHING"],
)
step_4_events_df = step_4_events_result.df()
step_4_events_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 14 columns):
 #   Column        Non-Null Count  Dtype              
---  ------        --------------  -----              
 0   start         418 non-null    datetime64[ns, UTC]
 1   end           418 non-null    datetime64[ns, UTC]
 2   id            418 non-null    object             
 3   type          418 non-null    object             
 4   position      418 non-null    object             
 5   regions       418 non-null    object             
 6   bounding_box  418 non-null    object             
 7   distances     418 non-null    object             
 8   vessel        418 non-null    object             
 9   encounter     0 non-null      object             
 10  fishing       414 non-null    object             
 11  gap           0 non-null      object             
 12  loitering     0 non-null      object             
 13  port_visit    4 non-null      object             
dtypes: datetime64[ns, UTC](2), object(12)
memory usage: 45.8+ KB
step_4_events_df["type"].value_counts()
type
fishing       414
port_visit      4
Name: count, dtype: int64

Explore Apparent Fishing Events

step_4_fishing_events_df = step_4_events_df[step_4_events_df["fishing"].notna()]
step_4_fishing_df = pd.concat(
    [
        pd.json_normalize(step_4_fishing_events_df["vessel"], sep="_"),
        pd.json_normalize(step_4_fishing_events_df["fishing"], sep="_"),
    ],
    axis=1,
)
step_4_fishing_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 414 entries, 0 to 413
Data columns (total 12 columns):
 #   Column                              Non-Null Count  Dtype  
---  ------                              --------------  -----  
 0   id                                  414 non-null    object 
 1   name                                389 non-null    object 
 2   ssvid                               414 non-null    object 
 3   flag                                414 non-null    object 
 4   type                                414 non-null    object 
 5   public_authorizations               414 non-null    object 
 6   nextPort                            0 non-null      object 
 7   total_distance_km                   414 non-null    float64
 8   average_speed_knots                 414 non-null    float64
 9   average_duration_hours              0 non-null      object 
 10  potential_risk                      414 non-null    bool   
 11  vessel_public_authorization_status  414 non-null    object 
dtypes: bool(1), float64(2), object(9)
memory usage: 36.1+ KB
step_4_fishing_df[
    [
        "name",
        "ssvid",
        "total_distance_km",
        "average_speed_knots",
    ]
]
name ssvid total_distance_km average_speed_knots
0 HUNG CHUAN SHUN 416007496 27.524249 8.183333
1 HUNG CHUAN SHUN 416007496 11.016940 6.285714
2 HUNG CHUAN SHUN 416007496 88.156534 4.255172
3 HUNG CHUAN SHUN 416007496 143.741059 5.758621
4 HUNG CHUAN SHUN 416007496 129.531733 4.850420
... ... ... ... ...
409 HUNG CHUAN SHUN 416007496 68.003416 5.202439
410 HUNG CHUAN SHUN 416007496 62.649188 4.577143
411 HUNG CHUAN SHUN 416007496 85.981416 4.420408
412 HUNG CHUAN SHUN 416007496 74.555684 3.927660
413 SENSHU MARU NO.3 431100690 12.164494 5.083333

414 rows × 4 columns

step_4_fishing_df["ssvid"].value_counts()
ssvid
416007496    363
431100690     26
412331032     25
Name: count, dtype: int64

Explore Port Visit Events

step_4_port_visit_events_df = step_4_events_df[step_4_events_df["port_visit"].notna()]
step_4_port_visits_df = pd.concat(
    [
        pd.json_normalize(step_4_port_visit_events_df["vessel"], sep="_"),
        pd.json_normalize(step_4_port_visit_events_df["port_visit"], sep="_"),
    ],
    axis=1,
)
step_4_port_visits_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4 entries, 0 to 3
Data columns (total 37 columns):
 #   Column                                         Non-Null Count  Dtype  
---  ------                                         --------------  -----  
 0   id                                             4 non-null      object 
 1   name                                           3 non-null      object 
 2   ssvid                                          4 non-null      object 
 3   flag                                           4 non-null      object 
 4   type                                           4 non-null      object 
 5   public_authorizations                          4 non-null      object 
 6   nextPort                                       0 non-null      object 
 7   visit_id                                       4 non-null      object 
 8   confidence                                     4 non-null      object 
 9   duration_hrs                                   4 non-null      float64
 10  start_anchorage_anchorage_id                   4 non-null      object 
 11  start_anchorage_at_dock                        4 non-null      bool   
 12  start_anchorage_distance_from_shore_km         4 non-null      float64
 13  start_anchorage_flag                           4 non-null      object 
 14  start_anchorage_id                             4 non-null      object 
 15  start_anchorage_lat                            4 non-null      float64
 16  start_anchorage_lon                            4 non-null      float64
 17  start_anchorage_name                           4 non-null      object 
 18  start_anchorage_top_destination                4 non-null      object 
 19  intermediate_anchorage_anchorage_id            4 non-null      object 
 20  intermediate_anchorage_at_dock                 4 non-null      bool   
 21  intermediate_anchorage_distance_from_shore_km  4 non-null      float64
 22  intermediate_anchorage_flag                    4 non-null      object 
 23  intermediate_anchorage_id                      4 non-null      object 
 24  intermediate_anchorage_lat                     4 non-null      float64
 25  intermediate_anchorage_lon                     4 non-null      float64
 26  intermediate_anchorage_name                    4 non-null      object 
 27  intermediate_anchorage_top_destination         4 non-null      object 
 28  end_anchorage_anchorage_id                     4 non-null      object 
 29  end_anchorage_at_dock                          4 non-null      bool   
 30  end_anchorage_distance_from_shore_km           4 non-null      float64
 31  end_anchorage_flag                             4 non-null      object 
 32  end_anchorage_id                               4 non-null      object 
 33  end_anchorage_lat                              4 non-null      float64
 34  end_anchorage_lon                              4 non-null      float64
 35  end_anchorage_name                             4 non-null      object 
 36  end_anchorage_top_destination                  4 non-null      object 
dtypes: bool(3), float64(10), object(24)
memory usage: 1.2+ KB
step_4_port_visits_df[
    [
        "name",
        "ssvid",
        "confidence",
        "start_anchorage_name",
        "intermediate_anchorage_name",
        "end_anchorage_name",
    ]
]
name ssvid confidence start_anchorage_name intermediate_anchorage_name end_anchorage_name
0 SENSHU MARU NO.3 431100690 4 TEMA TEMA TEMA
1 None 412331032 4 DAKAR DAKAR DAKAR
2 HUNG CHUAN SHUN 416007496 4 TEMA TEMA TEMA
3 SENSHU MARU NO.3 431100690 4 TEMA TEMA TEMA

What We’ve Learned from Step 4

  • 4: port visits, 0: encounters, and 414: fishing events were found for the queried vessels in the given date range.

  • Some events were missed due to AIS data coverage gaps.

  • Different filters may need to be applied to refine results.

Caveats & Considerations

  • A lack of recorded encounters or any other events does not confirm the absence of such activities—AIS coverage, reporting behavior, and dataset updates can impact results.

  • Further investigation may be required, including manual validation using historical data or consulting additional sources.

Summary of API Flow

  1. 4Wings API - Identify fishing effort by gear type in Ghanaian EEZ.

  2. 4Wings API - Retrieve vessel IDs for potential longliners.

  3. Vessels API - Fetch detailed vessel identity & ownership.

  4. Events API - Attempt to detect fleet activity (port visits, encounters, and apparent fishing events)