This tutorial introduces visualization options for vessel trajectories processed using AISdb, including AISdb's integrated web interface and alternative approaches with popular Python visualization packages. Practical examples were provided for each tool, illustrating how to process and visualize AISdb tracks effectively.
Internal visualization
AISdb provides an integrated data visualization feature through the aisdb.web_interface.visualize module, which allows users to generate interactive maps displaying vessel tracks. This built-in tool is designed for simplicity and ease of use, offering customizable visualizations directly from AIS data without requiring extensive setup.
Here is an example of using the web interface module to show queried data with colors. To display vessel tracks in a single color:
import aisdb
from datetime import datetime
from aisdb.database.dbconn import SQLiteDBConn
from aisdb import DBConn, DBQuery, DomainFromPoints
import nest_asyncio
nest_asyncio.apply()
dbpath='YOUR_DATABASE.db' # Define the path to your database
# Set the start and end times for the query
start_time = datetime.strptime("2018-01-01 00:00:00", '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime("2018-01-03 00:00:00", '%Y-%m-%d %H:%M:%S')
# Define a circle with a 100km radius around the location point
domain = DomainFromPoints(points=[(-63.6, 44.6)], radial_distances=[100000])
def color_tracks(tracks):
""" Set the color of each vessel track using a color name or RGB value. """
for track in tracks:
track['color'] = 'yellow'
yield track
with aisdb.SQLiteDBConn(dbpath=dbpath) as dbconn:
qry = aisdb.DBQuery(
dbconn=dbconn, start=start_time, end=end_time,
xmin=domain.boundary['xmin'], xmax=domain.boundary['xmax'],
ymin=domain.boundary['ymin'], ymax=domain.boundary['ymax'],
callback=aisdb.database.sqlfcn_callbacks.in_time_bbox_validmmsi,
)
rowgen = qry.gen_qry()
tracks = aisdb.track_gen.TrackGen(rowgen, decimate=False)
colored_tracks = color_tracks(tracks)
# Visualization
aisdb.web_interface.visualize(
colored_tracks,
domain=domain,
visualearth=True,
open_browser=True,
)
If you want to visualize vessel tracks in different colors based on MMSI, here's an example that demonstrates how to color-code tracks for easy identification:
import random
def color_tracks2(tracks):
colors = {}
for track in tracks:
mmsi = track.get('mmsi')
if mmsi not in colors:
# Assign a random color to this MMSI if not already assigned
colors[mmsi] = "#{:06x}".format(random.randint(0, 0xFFFFFF))
track['color'] = colors[mmsi] # Set the color for the current track
yield track
with aisdb.SQLiteDBConn(dbpath=dbpath) as dbconn:
qry = aisdb.DBQuery(
dbconn=dbconn, start=start_time, end=end_time,
xmin=domain.boundary['xmin'], xmax=domain.boundary['xmax'],
ymin=domain.boundary['ymin'], ymax=domain.boundary['ymax'],
callback=aisdb.database.sqlfcn_callbacks.in_time_bbox_validmmsi,
)
rowgen = qry.gen_qry()
tracks = aisdb.track_gen.TrackGen(rowgen, decimate=False)
colored_tracks = list(color_tracks2(tracks))
# Visualization
aisdb.web_interface.visualize(
colored_tracks,
domain=domain,
visualearth=True,
open_browser=True,
)
Alternative visualization
Several alternative Python packages can be leveraged for users seeking more advanced or specialized visualization capabilities. For instance, Basemap and Cartopy are excellent for creating detailed 2D plots, while Plotly offering powerful interactive graphs. Additionally, Kepler.gl caters to users needing dynamic, large-scale visualizations or 3D mapping. These alternatives allow for a deeper exploration of AIS data, offering flexibility in how data is presented and analyzed beyond the default capabilities of AISdb.
Basemap + Matplotlib
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
def plot_tracks_with_basemap(tracks):
plt.figure(figsize=(12, 8))
# Define the geofence boundaries
llcrnrlat = 42.854329883666175 # Latitude of the southwest corner
urcrnrlat = 47.13666808816243 # Latitude of the northeast corner
llcrnrlon = -68.73998377599209 # Longitude of the southwest corner
urcrnrlon = -56.92378296577808 # Longitude of the northeast corner
# Create the Basemap object with the geofence
m = Basemap(projection='merc',
llcrnrlat=llcrnrlat, urcrnrlat=urcrnrlat,
llcrnrlon=llcrnrlon, urcrnrlon=urcrnrlon, resolution='i')
m.drawcoastlines()
m.drawcountries()
m.drawmapboundary(fill_color='aqua')
m.fillcontinents(color='lightgreen', lake_color='aqua')
for track in tracks:
lons, lats = track['lon'], track['lat']
x, y = m(lons, lats)
m.plot(x, y, color=track['color'], linewidth=2)
plt.show()
with aisdb.SQLiteDBConn(dbpath=dbpath) as dbconn:
qry = aisdb.DBQuery(
dbconn=dbconn, start=start_time, end=end_time,
xmin=domain.boundary['xmin'], xmax=domain.boundary['xmax'],
ymin=domain.boundary['ymin'], ymax=domain.boundary['ymax'],
callback=aisdb.database.sqlfcn_callbacks.in_time_bbox_validmmsi,
)
rowgen = qry.gen_qry()
tracks = aisdb.track_gen.TrackGen(rowgen, decimate=False)
colored_tracks = list(color_tracks2(tracks))
plot_tracks_with_basemap(colored_tracks)
Cartopy
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
def plot_tracks_with_cartopy(tracks):
plt.figure(figsize=(12, 8))
ax = plt.axes(projection=ccrs.Mercator())
ax.coastlines()
for track in tracks:
lons, lats = track['lon'], track['lat']
ax.plot(lons, lats, transform=ccrs.PlateCarree(), color=track['color'], linewidth=2)
plt.title('AIS Tracks Visualization with Cartopy')
plt.show()
with aisdb.SQLiteDBConn(dbpath=dbpath) as dbconn:
qry = aisdb.DBQuery(
dbconn=dbconn, start=start_time, end=end_time,
xmin=domain.boundary['xmin'], xmax=domain.boundary['xmax'],
ymin=domain.boundary['ymin'], ymax=domain.boundary['ymax'],
callback=aisdb.database.sqlfcn_callbacks.in_time_bbox_validmmsi,
)
rowgen = qry.gen_qry()
tracks = aisdb.track_gen.TrackGen(rowgen, decimate=False)
colored_tracks = list(color_tracks2(tracks))
plot_tracks_with_cartopy(colored_tracks)
Plotly
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
def track2dataframe(tracks):
data = []
# Iterate over each track in the vessels_generator
for track in tracks:
# Unpack static information
mmsi = track['mmsi']
rot = track['rot']
maneuver = track['maneuver']
heading = track['heading']
# Unpack dynamic information
times = track['time']
lons = track['lon']
lats = track['lat']
cogs = track['cog']
sogs = track['sog']
utc_seconds = track['utc_second']
# Iterate over the dynamic arrays and create a row for each time point
for i in range(len(times)):
data.append({
'mmsi': mmsi,
'rot': rot,
'maneuver': maneuver,
'heading': heading,
'time': times[i],
'longitude': lons[i],
'latitude': lats[i],
'cog': cogs[i],
'sog': sogs[i],
'utc_second': utc_seconds[i],
})
# Convert the list of dictionaries to a pandas DataFrame
df = pd.DataFrame(data)
return df
def plotly_visualize(data, visual_type='lines'):
if (visual_type=='scatter'):
# Create a scatter plot for the vessel data points using scatter_geo
fig = px.scatter_geo(
data,
lat="latitude",
lon="longitude",
color="mmsi", # Color by vessel identifier
hover_name="mmsi",
hover_data={"time": True},
title="Vessel Data Points"
)
else:
# Create a line plot for the vessel trajectory using scatter_geo
fig = px.line_geo(
data,
lat="latitude",
lon="longitude",
color="mmsi", # Color by vessel identifier
hover_name="mmsi",
hover_data={"time": True},
)
# Set the map style and projection
fig.update_geos(
projection_type="azimuthal equal area", # Change this to 'natural earth', 'azimuthal equal area', etc.
showland=True,
landcolor="rgb(243, 243, 243)",
countrycolor="rgb(204, 204, 204)",
lonaxis=dict(range=[-68.73998377599209, -56.92378296577808]), # Longitude range (geofence)
lataxis=dict(range=[42.854329883666175, 47.13666808816243]) # Latitude range (geofence)
)
# Set the layout to focus on a specific area or zoom level
fig.update_layout(
geo=dict(
projection_type="mercator",
center={"lat": 44.5, "lon": -63.5},
),
width=900, # Increase the width of the plot
height=700, # Increase the height of the plot
)
fig.show()
with aisdb.SQLiteDBConn(dbpath=dbpath) as dbconn:
qry = aisdb.DBQuery(
dbconn=dbconn, start=start_time, end=end_time,
xmin=domain.boundary['xmin'], xmax=domain.boundary['xmax'],
ymin=domain.boundary['ymin'], ymax=domain.boundary['ymax'],
callback=aisdb.database.sqlfcn_callbacks.in_time_bbox_validmmsi,
)
rowgen = qry.gen_qry()
tracks = aisdb.track_gen.TrackGen(rowgen, decimate=False)
df = track2dataframe(tracks)
plotly_visualize(df, 'lines')