Dive log

A record of recreational and scientific diving, usually on shipwrecks.

I’m a qualified Commercial and Technical Diver (ADAS P1, TDI ANDP). I have been doing scientific diving since 2017, and I am comfortable working in challenging conditions including very low visibility (<1m) and breaking swell. I specialise in underwater photogrammetry for archaeological recording, much of which I have done with the WA Museum.

Below is my dive log. The data behind this post is available for download here. Please note some coordinates are slightly obfuscated, they are indicative only. Last updated January 2025.

Show code
library(tidyverse)
library(patchwork)
library(leaflet)
library(sf)
library(gt)
library(DT)
theme_set(theme_minimal())

dive_log <- read_csv("portfolio/dive_log.csv") %>% 
  mutate(across(c(
    category, entry, group, nitrox, night, water),
                as_factor))

dive_log %>% mutate(depth_bins = cut(depth,
                        breaks=c(0,10,20,30, 40),
                        labels=c('< 10m', '10-20m',
                                 '20-30m', '30-40m'))) %>% 
  group_by("depth range" = depth_bins) %>% 
  summarise(dives = n(),
            hours = round(sum(duration)/60,1)) %>% 
  pivot_longer(-`depth range`, names_to = 'depth') %>% 
  pivot_wider(names_from = `depth range`) %>% 
  gt() %>% tab_options(table.width = pct(100)) %>% 
  fmt_number(
    columns = 2:5,
    rows = 1,
    decimals = 0
  ) %>% 
  tab_caption(
    glue::glue(
      "{nrow(dive_log)} dives over {round(sum(dive_log$duration)/60,0)} hours. ",
      "{sum(!is.na(dive_log$nitrox), na.rm = TRUE)} on nitrox, ",
      "and {sum(!is.na(dive_log$night), na.rm = TRUE)} at night. ",
      "Deepest to {max(dive_log$depth, na.rm = TRUE)} m."
    )
  )
Table 1: 337 dives over 222 hours. 14 on nitrox, and 12 at night. Deepest to 38.2 m.
depth < 10m 10-20m 20-30m 30-40m
dives 152 122 52 11
hours 99.3 83.4 32.8 6.4
Show code
depth_hist <- ggplot(dive_log) + 
  geom_histogram(aes(depth), binwidth = 1,
                 fill = "#48497F", alpha = 0.9) +
  geom_vline(xintercept = 18, lty=2) +
  labs(
    title = paste0(nrow(dive_log),
                   " dives since 2015"),
    subtitle = paste0(sum(dive_log$depth > 18),
                      ' dives deeper than 18m'))

time_hist <- ggplot(dive_log) + 
  geom_histogram(aes(duration), binwidth = 5,
                 fill = "#48497F", alpha = 0.9) + 
  geom_vline(xintercept = mean(dive_log$duration), lty=2) +
  labs(
    title = paste0(round(sum(dive_log$duration)/60),
      " hours underwater"),
    subtitle = paste0("mean dive time is ",
                         round(mean(dive_log$duration)), ' mins'))

depth_hist + time_hist

Show code
map_labels <- paste0(dive_log$date, " ", dive_log$name)

dive_log %>%
  st_as_sf(coords=c('lon', 'lat')) %>% 
leaflet(options=leafletOptions(
    minZoom = 3,
    maxZoom = 10,
  )) %>% 
  setView(lng = 115, lat = -26, zoom = 4) %>% 
  addProviderTiles(provider = providers$CartoDB.Voyager) %>% 
  addCircleMarkers(radius = 7, label = map_labels,
                   stroke = FALSE, fillOpacity = .7,
                   clusterOptions = markerClusterOptions(
                     maxClusterRadius=30,
                     spiderfyDistanceMultiplier=1.2))
Show code
dive_log %>% 
  select(date, time, mins = duration,
         depth, place, category, name) %>% 
  mutate(time = substr(time,1,5)) %>% 
datatable(filter = 'top',
          class = 'compact nowrap',
          options = list(scrollX = TRUE,
                         pageLength=25,
  order = list(list(1, 'desc'),list(2, 'desc'))
))