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:
Analyzing apparent fishing effort, specifically for trawlers in Senegalese EEZ.
Identifying vessels involved in apparent trawling activity and determining their reported flag states.
Checking vessel history, including prior encounters (or potential transshipment) or port visits.
Generating reports for enforcement authorities to assess risks.
APIs Used: ️
4Wings API – To retrieve apparent fishing effort data for trawlers operating in Senegalese EEZ over the past 3 months.
Vessels API – To retrieve detailed vessel information, including
flag,ownership history, andauthorizations.
Important: In order to avoid any misinterpretation of GFW data, please refer to our official data caveats documentations:
Important Caveats:
The 4Wings API only supports one active report per user at a time.
Sending multiple requests simultaneously results in a 429 Too Many Requests error.
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:
Using Region ID - Each EEZ has a unique ID in the public-eez-areas dataset.
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:
Date Range - Last 3 Months
Grouped By - Vessel ID
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, andauthorizationsin 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:
Vessel IDs from 4Wings API, Step 1 above.
Datasets -
public-global-vessel-identity:latest.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, andflagas 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, orownershipare relevant for enforcement.Generate an apparent activity report with all available details.
Summary of API Flow¶
4Wings API - Retrieve apparent fishing effort for trawlers within Senegalese EEZ.
Vessels API - Fetch detailed vessel identity, ownership history, and public authorizations for vessels detected in Step 1.
Analyze vessel history - Compare registry records, AIS self-reported data, and inferred information to identify potential flag-hopping or historical changes in vessel identity.
Assess authorizations - Cross-check whether vessels have publicly available fishing authorizations and consider external official sources for further verification.
Generate an analysis report - Provide enforcement authorities with a structured report highlighting vessel activity, identity records, and any notable discrepancies for further investigation.