Open In Colab

Analyze apparent fishing effort in Senegalese EEZ

This guide provides detailed instructions to on how to use the gfw-api-python-client to Analyze apparent fishing effort in Senegalese EEZ region and monitor vessel activities using 4Wings API, and Vessels 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: A Port Inspector Monitoring Vessel Activity

Mamadou, a port inspector in Dakar, Senegal, monitors vessel activity within Senegalese Exclusive Economic Zone (EEZ). His goal is to:

  1. Analyzing apparent fishing effort, specifically for trawlers in Senegalese EEZ.

  2. Identifying vessels involved in apparent trawling activity and determining their reported flag states.

  3. Checking vessel history, including prior encounters (or potential transshipment) or port visits.

  4. Generating reports for enforcement authorities to assess risks.

APIs Used:

  1. 4Wings API – To retrieve apparent fishing effort data for trawlers operating in Senegalese EEZ over the past 3 months.

  2. Vessels API – To retrieve detailed vessel information, including flag, ownership history, and authorizations.

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) - Senegalese EEZ

Before making API requests, Mamadou 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 Senegalese EEZ, the region ID is 8371 (public-eez-areas dataset).

Step 1: Retrieve Apparent Fishing Effort in Senegalese EEZ

Mamadou first queries the 4Wings API to get apparent fishing effort for all vessels, grouping them by vessel ID in Senegalese EEZ. Please learn more about apparent fishing effort here and check its data caveats here.

Filters Used:

  1. Region ID - 8371 Senegalese EEZ

  2. Date Range - Last 3 Months

  3. Grouped By - Vessel ID

  4. Gear Type - Trawlers

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 2 below.

Explanation of Parameters & Considerations

  • Gear types, such as trawlers, are inferred based on Global Fishing Watch’s vessel classification system, which relies on AIS data and vessel public registries. The gear type associated with each vessel is not always 100% accurate, as it may be derived from historical sources or inferred from movement patterns. See more details on supported gear types here.

  • Also, please see data caveats regarding vessel types and their classification here.

  • See more details on retrieving Region IDs here.

step_1_report_result = await gfw_client.fourwings.create_fishing_effort_report(
    spatial_resolution="HIGH",
    group_by="VESSEL_ID",
    temporal_resolution="MONTHLY",
    filters=["geartype in ('trawlers')"],
    start_date="2024-11-01",
    end_date="2025-01-31",
    spatial_aggregation=True,
    region={
        "dataset": "public-eez-areas",
        "id": "8371",
    },
)
step_1_report_df = step_1_report_result.df()
step_1_report_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 170 entries, 0 to 169
Data columns (total 20 columns):
 #   Column                   Non-Null Count  Dtype              
---  ------                   --------------  -----              
 0   date                     170 non-null    object             
 1   detections               0 non-null      object             
 2   flag                     170 non-null    object             
 3   gear_type                170 non-null    object             
 4   hours                    170 non-null    float64            
 5   vessel_ids               0 non-null      object             
 6   vessel_id                170 non-null    object             
 7   vessel_type              170 non-null    object             
 8   entry_timestamp          170 non-null    datetime64[ns, UTC]
 9   exit_timestamp           170 non-null    datetime64[ns, UTC]
 10  first_transmission_date  170 non-null    datetime64[ns, UTC]
 11  last_transmission_date   170 non-null    datetime64[ns, UTC]
 12  imo                      170 non-null    object             
 13  mmsi                     170 non-null    object             
 14  call_sign                170 non-null    object             
 15  dataset                  170 non-null    object             
 16  report_dataset           170 non-null    object             
 17  ship_name                170 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: 26.7+ KB
step_1_report_df[["flag", "gear_type", "hours", "mmsi", "ship_name"]].head()
flag gear_type hours mmsi ship_name
0 CHN TRAWLERS 31.888889 412444322 MIN LONG YU61146
1 SEN TRAWLERS 524.815556 663093000 AMINE
2 SEN TRAWLERS 612.825556 663123000 ILE AUX OISEAUX
3 CHN TRAWLERS 0.368056 412209175 MENGXIN24
4 ESP TRAWLERS 1.306389 225987981 CIUDAD DE HUELVA

Explore Vessels Potentially Engaged in Trawling Activity in the Senegalese EEZ

step_1_agg_report_df = (
    step_1_report_df.groupby(["flag", "gear_type", "mmsi", "ship_name"], as_index=False)
    .agg(hours=("hours", "sum"))
    .sort_values(by="hours", ascending=False)
)
step_1_agg_report_df.head()
flag gear_type mmsi ship_name hours
66 SEN TRAWLERS 663178000 NUEVONOSOLAR 1678.888333
52 SEN TRAWLERS 663115000 BETTY 1648.771944
49 SEN TRAWLERS 663112000 TADORNE 1610.828611
42 SEN TRAWLERS 663039000 SEGUNDO SAN RAFAEL 1595.538333
74 SEN TRAWLERS 663250000 PRAIA DA MAROSA 1573.587500

What We have Learned from Step 1

  • There are vessels appear to have been engaged in potential trawling activity in Senegalese EEZ over the past 3 months i.e.,:

    • NUEVONOSOLAR (mmsi: 663178000, flag: SEN)

    • BETTY (mmsi: 663115000, flag: SEN)

  • We will retrieve these vessels’ ownership, flag history, and authorizations in Step 2 to validate them.

Step 2: Retrieve Vessel Details Using the Vessels API

Mamadou 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 1 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_1_vessel_mmsis = list(step_1_agg_report_df["mmsi"].head(n=2))
step_1_vessel_mmsis
['663178000', '663115000']
step_1_vessel_ids = list(
    step_1_report_df[step_1_report_df["mmsi"].isin(step_1_vessel_mmsis)][
        "vessel_id"
    ].unique()
)
step_1_vessel_ids
['894bc3ec6-6ade-f09c-e792-ff2e947508d8',
 'bf28c5a58-8c83-8690-8689-7f2d520f926e']
step_2_vessels_result = await gfw_client.vessels.get_vessels_by_ids(
    ids=step_1_vessel_ids,
)
step_2_vessels_df = step_2_vessels_result.df()
step_2_vessels_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2 entries, 0 to 1
Data columns (total 7 columns):
 #   Column                          Non-Null Count  Dtype 
---  ------                          --------------  ----- 
 0   dataset                         2 non-null      object
 1   registry_info_total_records     2 non-null      int64 
 2   registry_info                   2 non-null      object
 3   registry_owners                 2 non-null      object
 4   registry_public_authorizations  2 non-null      object
 5   combined_sources_info           2 non-null      object
 6   self_reported_info              2 non-null      object
dtypes: int64(1), object(6)
memory usage: 244.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_2_vessels_df[["registry_info", "registry_owners", "self_reported_info"]]
registry_info registry_owners self_reported_info
0 [{'id': '199483471cd2da3717552fddb1a3172a', 's... [{'name': 'ARMEMENT SOPASEN', 'flag': 'SEN', '... [{'id': '894bc3ec6-6ade-f09c-e792-ff2e947508d8...
1 [{'id': '29fef17154387858d8d4c777311c57f7', 's... [{'name': 'SENEVISA', 'flag': 'ESP', 'ssvid': ... [{'id': 'bf28c5a58-8c83-8690-8689-7f2d520f926e...

Explore Vessels Registry Info

step_2_registry_info_df = pd.json_normalize(
    step_2_vessels_df["registry_info"].explode()
)
step_2_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 663115000 SEN BETTY BETTY [TRAWLERS] [IMO, TMT_NATIONAL, TMT_OTHER_OFFICIAL]
1 663178000 SEN NUEVO NOSO LAR NUEVONOSOLAR [TRAWLERS] [IMO, TMT_NATIONAL, TMT_OTHER_OFFICIAL]
2 762178000 SEN NUEVO NOSO LAR NUEVONOSOLAR [TRAWLERS] [IMO, TMT_NATIONAL, TMT_OTHER_OFFICIAL]
3 552178000 SEN NUEVO NOSO LAR NUEVONOSOLAR [TRAWLERS] [IMO, TMT_NATIONAL, TMT_OTHER_OFFICIAL]

Explore Registry Owners

step_2_registry_owners_df = pd.json_normalize(
    step_2_vessels_df["registry_owners"].explode()
)
step_2_registry_owners_df[["ssvid", "flag", "name", "source_code"]]
ssvid flag name source_code
0 663115000 SEN ARMEMENT SOPASEN [TMT_NATIONAL, TMT_OTHER_OFFICIAL]
1 663178000 ESP SENEVISA [TMT_NATIONAL, TMT_OTHER_OFFICIAL]
2 663176000 ESP SENEVISA [TMT_NATIONAL, TMT_OTHER_OFFICIAL]
3 762178000 ESP SENEVISA [TMT_NATIONAL, TMT_OTHER_OFFICIAL]
4 552178000 ESP SENEVISA [TMT_NATIONAL, TMT_OTHER_OFFICIAL]

Explore Vessels Self Reported Info

step_2_self_reported_info_df = pd.json_normalize(
    step_2_vessels_df["self_reported_info"].explode()
)
step_2_self_reported_info_df[
    ["ssvid", "flag", "ship_name", "n_ship_name", "source_code"]
]
ssvid flag ship_name n_ship_name source_code
0 663115000 SEN BETTY BETTY [AIS]
1 663178000 SEN NUEVONOSOLAR NUEVONOSOLAR [AIS]
2 663178000 SEN NUEVO=NOSOLAR+3&!U.? NUEVONOSOLAR3U [AIS]
3 762178000 None NUEVO NOSOLAR NUEVONOSOLAR [AIS]
4 552178000 None NUEVO NOSOLAR NUEVONOSOLAR [AIS]
5 663178000 SEN NUEVO NOSOLAR NUEVONOSOLAR [AIS]

What We have Learned from Step 2

  • Vessel Identity:

    • NUEVONOSOLAR (mmsi: 663178000, flag: SEN)- appears to be registered under Senegal (SEN)

    • BETTY (mmsi: 663115000, flag: SEN) - appears to be registered under Senegal (SEN)

  • Ownership & Historical Changes:

    • NUEVONOSOLAR (mmsi: 663178000, flag: SEN) - SENEVISA appears to be listed as the registered owner.

    • BETTY (mmsi: 663115000, flag: SEN)- ARMEMENT SOPASEN appears to be listed as the registered owner.

Next Steps:

  • Further, validate ownership history using official registry sources.

  • Assess whether any historical changes in flag, name, or ownership are relevant for enforcement.

  • Generate an apparent activity report with all available details.

Summary of API Flow

  1. 4Wings API - Retrieve apparent fishing effort for trawlers within Senegalese EEZ.

  2. Vessels API - Fetch detailed vessel identity, ownership history, and public authorizations for vessels detected in Step 1.

  3. Analyze vessel history - Compare registry records, AIS self-reported data, and inferred information to identify potential flag-hopping or historical changes in vessel identity.

  4. Assess authorizations - Cross-check whether vessels have publicly available fishing authorizations and consider external official sources for further verification.

  5. Generate an analysis report - Provide enforcement authorities with a structured report highlighting vessel activity, identity records, and any notable discrepancies for further investigation.