Overview:
The Ocean Wise Sightings Network API provides access to high-quality, cleaned, and collated whale presence data to support conservation efforts, policymaking, environmental assessments, and marine operations from the Ocean Wise Sightings Network database (OWSN).
This service is designed to facilitate easy access to marine mammal presence data for conservation purposes, while maintaining ethical data-sharing practices and supporting contributors.
Our API delivers real-time and historical whale presence data, processed and structured for seamless integration into existing workflows. Users benefit from:
Cleaned and validated data
Standardized API access with documentation and sample code (R, Python, VBA, Power BI) for access, data visualization, and analysis
QGIS:
Access
To access the API directly in QGIS and visualize data you will utilize the QGIS Python Console.
The QGIS Python Console is an interactive shell for Python command executions. It also has a Python file editor that allows you to edit and save your Python scripts.
To open the console go to Plugins ► Python Console (Ctrl+Alt+P). Once opened, it will look like the image below.
Click the "Show editor" button in the console to bring up a blank script. Here you will enter and run your code. Your session should now look like the below image (you may have to drag and rearrange the size of the console and windows to view the script editor).
Copy the code below ► paste into the script editor. NOTE - you will need to update the
API_KEY
,DATEFROM
, andDATETO
for the code to work (see below callout for a warning before you run this).#```python
import requests
import json
from qgis.core import QgsVectorLayer, QgsField, QgsFeature, QgsPointXY, QgsGeometry, QgsProject, QgsCategorizedSymbolRenderer, QgsRendererCategory, QgsSymbol
from PyQt5.QtCore import QVariant
from PyQt5.QtGui import QColor
# API Request
BASE_URL = "https://sightingsapi.ocean.org/sightings"
API_KEY = "your_api_key_here" # REPLACE WITH GIVEN API KEY
LIMIT = 1000
PAGE = 1
DATEFROM = "2025-01-01T00:00:00Z" # UPDATE THIS
DATETO = "2025-03-01T00:00:00Z" # UPDATE THIS
all_data = []
params = {
"api_key": API_KEY,
"limit": LIMIT,
"page": PAGE,
"sightedFrom": DATEFROM,
"sightedTo": DATETO
}
headers = {"x-api-key": API_KEY}
while True:
response = requests.get(BASE_URL, params=params, headers=headers)
if response.status_code != 200:
print(f"Error: {response.status_code}")
break
data = response.json()
records = data.get("sightings", [])
if not records:
break
all_data.extend(records)
PAGE += 1
params["page"] = PAGE
# Species Lookup Table (speciesID → speciesName)
species_mapping = {
1: "Killer whale",
2: "Humpback whale",
3: "Grey whale",
4: "Minke whale",
5: "Fin whale",
6: "Sperm whale",
7: "Blue whale",
8: "Sei whale",
9: "North Pacific right whale",
10: "Baird's beaked whale",
11: "Cuvier's beaked whale",
12: "Other rare species",
13: "Unidentified whale",
14: "Killer whale",
15: "Harbour porpoise",
16: "Dall's porpoise",
17: "Pacific white-sided dolphin",
18: "Risso's dolphin",
19: "Northern right whale dolphin",
20: "False killer whale",
21: "Common dolphin (short or long beaked)",
22: "Unidentified dolphin or porpoise",
23: "Leatherback sea turtle",
24: "Green sea turtle",
25: "Olive ridley sea turtle",
26: "Loggerhead sea turtle",
27: "Unidentified sea turtle",
}
# Create Memory Layer in QGIS
layer = QgsVectorLayer("Point?crs=EPSG:4326", "Whale Sightings", "memory")
provider = layer.dataProvider()
# Add fields (columns)
provider.addAttributes([
QgsField("speciesID", QVariant.Int),
QgsField("speciesName", QVariant.String),
QgsField("sightedDate", QVariant.String),
QgsField("travelDirection", QVariant.String),
QgsField("ecotype", QVariant.String),
QgsField("sourceEntity", QVariant.String),
QgsField("idConfidence", QVariant.String)
])
layer.updateFields()
# Process and Add Data Points
features = []
for record in all_data:
try:
lon = float(record.get("longitude", 0))
lat = float(record.get("latitude", 0))
# Skip invalid coordinates
if lon == 0 or lat == 0:
print(f"Skipping invalid coordinates: {record}")
continue
species_id = int(record.get("speciesId", 0)) # Convert ID to int
species_name = species_mapping.get(species_id, "Other rare species") # Default to "Other rare species"
direction = record.get("travelDirection", "Unknown")
ecotype = record.get("ecotype", "Unknown")
source_entity = record.get("sourceEntity", "Unknown")
id_confidence = record.get("idConfidence", "Unknown")
feature = QgsFeature()
point = QgsPointXY(lon, lat)
feature.setGeometry(QgsGeometry.fromPointXY(point))
feature.setAttributes([
species_id,
species_name,
record.get("sightedDate", "N/A"),
direction,
ecotype,
source_entity,
id_confidence
])
features.append(feature)
except Exception as e:
print(f"Error processing record: {record}\n{e}")
if features:
provider.addFeatures(features)
layer.updateExtents()
QgsProject.instance().addMapLayer(layer)
print(f"✅ Loaded {len(features)} whale sightings into QGIS.")
else:
print("⚠️ No valid data points were loaded. Check API response.")
layer.commitChanges()
layer.updateExtents()
QgsProject.instance().addMapLayer(layer)
# Define Color Mapping for Species
species_colors = {
"Killer whale": "black",
"Humpback whale": "red",
"Grey whale": "gray",
"Minke whale": "lightblue",
"Fin whale": "#A7C796" # Light moss green
}
# Create Categorized Symbol Renderer
categories = []
for species_id, species_name in species_mapping.items():
# Default color: purple (for "Other Rare Species")
color = "purple"
# Assign color based on species name
if species_name in species_colors:
color = species_colors[species_name]
elif "dolphin" in species_name.lower() or "porpoise" in species_name.lower():
color = "blue"
elif "turtle" in species_name.lower():
color = "darkgreen"
# Create symbol
symbol = QgsSymbol.defaultSymbol(layer.geometryType())
symbol.setColor(QColor(color))
# Create category using speciesID
category = QgsRendererCategory(species_id, symbol, species_name)
categories.append(category)
# Apply color renderer based on speciesID field
renderer = QgsCategorizedSymbolRenderer("speciesID", categories)
layer.setRenderer(renderer)
layer.triggerRepaint()
#```Click the green Run Script button.
Data should now be displayed in QGIS as points on the map.
Be Aware: Submitting a large query to the API via QGIS may lead to your system crashing given the large amount of data points. Recommended to save work before querying, and ensure that the DATEFROM
and DATETO
variables are assigned to a period you are interested in. The smaller the period, the faster the query.
Adding a Basemap
To see the points overlayed with context, you will need to add a basemap:
Go to Plugins → Manage and Install Plugins.
Search for QuickMapServices and install it.
Once installed, go to Web ► QuickMapServices ► OSM ► OSM Standard (or choose another basemap like Google Satellite / OpenStreetMap Standard.).
The basemap will now appear beneath your whale sightings layer.
Applying Filters in QGIS
Now your data is in QGIS, you may want to filter the data by species or other parameters (e.g., only showing Killer Whales):
Open the Attribute Table
Right-click on layer ► Open Attribute Table.
Click on Select by Expression.
Use the following expression:
Click Select Features to highlight those records.
To create a new layer with only the filtered features, go to Layer ► Export ► Save Selected Features As…
Filter by Drawing an Area
To filter by area:
Activate the Select Features by Polygon tool.
Draw an area around the sightings you want to filter.
Click Filter Selected Features to keep only the selected points.
Resources
More information on how to use QGIS features can be found online, these resources will get you started: