---
title: "2010/11 event"
author: "Clements et al., 2025"
date: "`r Sys.Date()`"
output: html_document
---
# ===========================================
# eReefs case studies: salinity plume mapping
# ===========================================

#Packages
```{r}
library(RNetCDF)
library(dplyr)
library(ggplot2)
library(sf)
library(leaflet)
library(rnaturalearth)
library(rnaturalearthdata)
library(tibble)
library(cowplot)
library(scales)
library(lubridate)
library(purrr)
```

## Salinity maps
### 2011
```{r}
#Notes: The code presented provides an example for generating outputs for the first day of the modelled time series. For the 2011 flood event, plume dynamics were mapped for the following dates: 22nd February, and 2, 6, 8, 10, 12 and 14 March 2011. The same code can be copied and applied to eReefs datasets representing each of these dates to generate the corresponding plume visualisations and analyses.
```

#### Normanby 
```{r}
# ===================
# Supplementary Fig 1
# ===================

#22.02.2011
# ---- Turn off s2 early (re-enable on exit) ----
old_s2 <- sf_use_s2(FALSE)
on.exit(sf_use_s2(old_s2), add = TRUE)

# ---- Date tag (single source of truth) ----
date_tag <- "2011-02-22"

# ---- Static window ----
xlim <- c(142.9, 144.8)
ylim <- c(-14.7, -11.5)
bbox <- st_bbox(c(xmin = xlim[1], ymin = ylim[1], xmax = xlim[2], ymax = ylim[2]), crs = st_crs(4326))

# ---- Legend remover ----
rm_leg <- function(p) p + theme(legend.position = "none") +
  guides(colour = "none", color = "none", fill = "none")

# ---- Land (QLD) ----
states <- rnaturalearth::ne_states(country = "Australia", returnclass = "sf")
qld <- states[grepl("Queensland", states$name, TRUE) |
              (("name_en" %in% names(states)) & grepl("Queensland", states$name_en, TRUE)), ] |>
  st_crop(bbox)

# ---- Reefs ----
use_cache  <- TRUE
cache_file <- file.path(tempdir(), sprintf("gbr_features_%0.1f-%0.1f_%0.1f-%0.1f.rds", xlim[1], xlim[2], ylim[1], ylim[2]))

if (use_cache && file.exists(cache_file)) {
  gbr <- readRDS(cache_file)
} else {
  wfs_base <- "https://maps.eatlas.org.au/maps/wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=ea:GBR_GBRMPA_GBR-features&outputFormat=application/json&srsName=EPSG:4326"
  bbox_q   <- sprintf("&bbox=%f,%f,%f,%f,EPSG:4326", xlim[1], ylim[1], xlim[2], ylim[2])
  gbr_raw <- tryCatch(
    st_read(paste0(wfs_base, bbox_q), quiet = TRUE),
    error = function(e) st_read(wfs_base, quiet = TRUE)
  )
  gbr_fix <- suppressWarnings(
    if ("st_make_valid" %in% ls("package:sf")) sf::st_make_valid(gbr_raw) else st_buffer(gbr_raw, 0)
  )
  gbr <- gbr_fix |> st_crop(bbox) |> st_collection_extract("POLYGON", warn = FALSE)
  if (use_cache) { dir.create(dirname(cache_file), showWarnings = FALSE, recursive = TRUE); saveRDS(gbr, cache_file) }
}

# ---- Palette & transform ----
salinity_cols <- colorRampPalette(c(rgb(48,18,59, maxColorValue = 255), "red","orange","yellow","cyan","blue"))
sal_stretch_trans <- scales::trans_new(
  "sal_stretch",
  transform = function(x) ifelse(x <= 30, x * (0.8/30), 0.8 + (x - 30) * (0.2/6)),
  inverse   = function(y) ifelse(y <= 0.8, y * (30/0.8), 30 + (y - 0.8) * (6/0.2))
)

# ---- Open NetCDF ----
ncfile_alts <- c(
  sprintf("https://thredds.nci.org.au/thredds/dodsC/fx3/gbr4_H4p0_ABARRAr2_OBRAN2020_FG2Gv3_Dhnd/gbr4_simple_%s.nc", date_tag)
)

open_nc_safely <- function(urls) {
  for (u in urls) {
    con <- try(open.nc(u), silent = TRUE)
    if (!inherits(con, "try-error")) return(con)
  }
  stop("All OPeNDAP endpoints failed to open.")
}
nc <- open_nc_safely(ncfile_alts); on.exit(close.nc(nc), add = TRUE)

lon <- var.get.nc(nc, "longitude")
lat <- var.get.nc(nc, "latitude")
zc  <- var.get.nc(nc, "zc")

# ---- Safe getter ----
safe_var_get <- function(nc, var, start, count, retries = 6, base_wait = 1.5) {
  for (i in seq_len(retries)) {
    out <- try(var.get.nc(nc, var, start = start, count = count), silent = TRUE)
    if (!inherits(out, "try-error")) return(out)
    Sys.sleep(base_wait * (2^(i - 1)))
  }
  stop("OPeNDAP read failed after retries.")
}

# ---- Map builder ----
make_map <- function(depth_m, time_idx) {
  k_idx <- which.min(abs(zc - (-depth_m)))
  salt  <- safe_var_get(nc, "salt",
                        start = c(1, 1, k_idx, time_idx),
                        count = c(dim(lon)[1], dim(lon)[2], 1, 1))

  df <- data.frame(lon = as.vector(lon),
                   lat = as.vector(lat),
                   salt = as.vector(salt)) |>
        dplyr::filter(!is.na(salt) & salt < 1e30,
                      dplyr::between(lat, ylim[1], ylim[2]),
                      dplyr::between(lon, xlim[1], xlim[2]))

  ggplot() +
    geom_sf(data = qld, fill = "#f6f2ee", color = NA) +
    geom_point(
      data = df,
      mapping = aes(x = lon, y = lat, color = pmin(pmax(salt, 0), 36)),
      shape = 15, size = 3, stroke = 0
    ) +
    scale_color_gradientn(
      colours = salinity_cols(256),
      limits  = c(0, 38),
      oob     = scales::squish,
      name    = "Salinity (PSU)",
      breaks  = seq(0, 38, by = 5),
      trans   = sal_stretch_trans
    ) +
    geom_sf(data = gbr, fill = NA, color = "black", size = 0.2, inherit.aes = FALSE) +
    coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
    scale_x_continuous(breaks = seq(143.5, 144.5, 1)) +
    scale_y_continuous(breaks = seq(-14.7, -11.5, 1)) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text  = element_text(size = 22),
      axis.ticks = element_line(color = "black"),
      plot.margin = margin(0, 0, 0, 0)
    )
}

# ---- Depths & times ----
depths      <- c(0.5, 3.00, 5.55, 8.80, 12.75)
depth_tags  <- c("0p5","3p00","5p55","8p80","12p75")
time_tags   <- c("0000","1200")
time_map    <- c("0000" = 1, "1200" = 13)

# ---- Generate plots ----
all_plots <- setNames(vector("list", length(time_tags) * length(depth_tags)),
  as.vector(outer(time_tags, depth_tags, function(tt, dd) paste0(date_tag, "_", tt, "_Normanby_", dd))))

for (ttag in names(time_map)) {
  t_idx <- time_map[[ttag]]
  panels <- setNames(
    mapply(function(d, dtag) make_map(d, t_idx), depths, depth_tags, SIMPLIFY = FALSE),
    paste0(date_tag, "_", ttag, "_Normanby_", depth_tags)
  )
  all_plots[names(panels)] <- panels
}

rows_nl <- lapply(depth_tags, function(dd) lapply(time_tags, function(tt) rm_leg(all_plots[[paste0(date_tag, "_", tt, "_Normanby_", dd)]])))
figure_depth_by_time <- cowplot::plot_grid(plotlist = unlist(rows_nl, recursive = FALSE),
                                           ncol = length(time_tags), align = "hv", axis = "tblr")

# ---- Panel aspect ----
panel_aspect <- diff(ylim) / (diff(xlim) * cos(pi * mean(ylim) / 180))
panel_w <- 4
panel_h <- panel_w * panel_aspect

ncol <- 2
nrow <- length(depth_tags)

out_file <- file.path(
  '[insert filepath]',
  paste0(date_tag, "_", ".png")
)

ggsave(
  filename  = out_file,
  plot      = figure_depth_by_time,
  width     = ncol * panel_w,
  height    = nrow * panel_h,
  dpi       = 300,
  limitsize = FALSE,
  bg        = "white"
)

# Open externally to avoid RStudio's Web/Plotly viewer
invisible(try(utils::browseURL(out_file), silent = TRUE))
cat("Plot saved to:", out_file, "\n")
```

#### COTS outbreak initiation zone
```{r}
# ===================
# Supplementary Fig 1
# ===================

# ---- Turn off s2 early (re-enable on exit) ----
old_s2 <- sf_use_s2(FALSE)
on.exit(sf_use_s2(old_s2), add = TRUE)

# ---- Date tag (single source of truth) ----
date_tag <- "2011-02-22"

# ---- Static window ----
xlim <- c(144.7, 146.5)
ylim <- c(-17.5, -14.5)
bbox <- st_bbox(c(xmin = xlim[1], ymin = ylim[1], xmax = xlim[2], ymax = ylim[2]), crs = st_crs(4326))

# ---- Legend remover ----
rm_leg <- function(p) p + theme(legend.position = "none") +
  guides(colour = "none", color = "none", fill = "none")

# ---- Land (QLD) ----
states <- rnaturalearth::ne_states(country = "Australia", returnclass = "sf")
qld <- states[grepl("Queensland", states$name, TRUE) |
              (("name_en" %in% names(states)) & grepl("Queensland", states$name_en, TRUE)), ] |>
  st_crop(bbox)

# ---- Reefs ----
use_cache  <- TRUE
cache_file <- file.path(tempdir(), sprintf("gbr_features_%0.1f-%0.1f_%0.1f-%0.1f.rds", xlim[1], xlim[2], ylim[1], ylim[2]))

if (use_cache && file.exists(cache_file)) {
  gbr <- readRDS(cache_file)
} else {
  wfs_base <- "https://maps.eatlas.org.au/maps/wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=ea:GBR_GBRMPA_GBR-features&outputFormat=application/json&srsName=EPSG:4326"
  bbox_q   <- sprintf("&bbox=%f,%f,%f,%f,EPSG:4326", xlim[1], ylim[1], xlim[2], ylim[2])
  gbr_raw <- tryCatch(
    st_read(paste0(wfs_base, bbox_q), quiet = TRUE),
    error = function(e) st_read(wfs_base, quiet = TRUE)
  )
  gbr_fix <- suppressWarnings(
    if ("st_make_valid" %in% ls("package:sf")) sf::st_make_valid(gbr_raw) else st_buffer(gbr_raw, 0)
  )
  gbr <- gbr_fix |> st_crop(bbox) |> st_collection_extract("POLYGON", warn = FALSE)
  if (use_cache) { dir.create(dirname(cache_file), showWarnings = FALSE, recursive = TRUE); saveRDS(gbr, cache_file) }
}

# ---- Palette & transform ----
salinity_cols <- colorRampPalette(c(rgb(48,18,59, maxColorValue = 255), "red","orange","yellow","cyan","blue"))
sal_stretch_trans <- scales::trans_new(
  "sal_stretch",
  transform = function(x) ifelse(x <= 30, x * (0.8/30), 0.8 + (x - 30) * (0.2/6)),
  inverse   = function(y) ifelse(y <= 0.8, y * (30/0.8), 30 + (y - 0.8) * (6/0.2))
)

# ---- Open NetCDF ----
ncfile_alts <- c(
  sprintf("https://thredds.nci.org.au/thredds/dodsC/fx3/gbr4_H4p0_ABARRAr2_OBRAN2020_FG2Gv3_Dhnd/gbr4_simple_%s.nc", date_tag)
)

open_nc_safely <- function(urls) {
  for (u in urls) {
    con <- try(open.nc(u), silent = TRUE)
    if (!inherits(con, "try-error")) return(con)
  }
  stop("All OPeNDAP endpoints failed to open.")
}
nc <- open_nc_safely(ncfile_alts); on.exit(close.nc(nc), add = TRUE)

lon <- var.get.nc(nc, "longitude")
lat <- var.get.nc(nc, "latitude")
zc  <- var.get.nc(nc, "zc")

# ---- Safe getter ----
safe_var_get <- function(nc, var, start, count, retries = 6, base_wait = 1.5) {
  for (i in seq_len(retries)) {
    out <- try(var.get.nc(nc, var, start = start, count = count), silent = TRUE)
    if (!inherits(out, "try-error")) return(out)
    Sys.sleep(base_wait * (2^(i - 1)))
  }
  stop("OPeNDAP read failed after retries.")
}

# ---- Map builder ----
make_map <- function(depth_m, time_idx) {
  k_idx <- which.min(abs(zc - (-depth_m)))
  salt  <- safe_var_get(nc, "salt",
                        start = c(1, 1, k_idx, time_idx),
                        count = c(dim(lon)[1], dim(lon)[2], 1, 1))

  df <- data.frame(lon = as.vector(lon),
                   lat = as.vector(lat),
                   salt = as.vector(salt)) |>
        dplyr::filter(!is.na(salt) & salt < 1e30,
                      dplyr::between(lat, ylim[1], ylim[2]),
                      dplyr::between(lon, xlim[1], xlim[2]))

  ggplot() +
    geom_sf(data = qld, fill = "#f6f2ee", color = NA) +
    geom_point(
      data = df,
      mapping = aes(x = lon, y = lat, color = pmin(pmax(salt, 0), 36)),
      shape = 15, size = 4, stroke = 0
    ) +
    scale_color_gradientn(
      colours = salinity_cols(256),
      limits  = c(0, 38),
      oob     = scales::squish,
      name    = "Salinity (PSU)",
      breaks  = seq(0, 38, by = 5),
      trans   = sal_stretch_trans
    ) +
    geom_sf(data = gbr, fill = NA, color = "black", size = 0.2, inherit.aes = FALSE) +
    coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
    scale_x_continuous(breaks = seq(145, 146, 1)) +
    scale_y_continuous(breaks = seq(-17, -15, 1)) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text  = element_text(size = 22),
      axis.ticks = element_line(color = "black"),
      plot.margin = margin(0, 0, 0, 0)
    )
}

# ---- Depths & times ----
depths      <- c(0.5, 3.00, 5.55, 8.80, 12.75)
depth_tags  <- c("0p5","3p00","5p55","8p80","12p75")
time_tags   <- c("0000","1200")
time_map    <- c("0000" = 1, "1200" = 13)

# ---- Generate plots ----
all_plots <- setNames(vector("list", length(time_tags) * length(depth_tags)),
  as.vector(outer(time_tags, depth_tags, function(tt, dd) paste0(date_tag, "_", tt, "_Normanby_", dd))))

for (ttag in names(time_map)) {
  t_idx <- time_map[[ttag]]
  panels <- setNames(
    mapply(function(d, dtag) make_map(d, t_idx), depths, depth_tags, SIMPLIFY = FALSE),
    paste0(date_tag, "_", ttag, "_COTS outbreak initiation zone_", depth_tags)
  )
  all_plots[names(panels)] <- panels
}

rows_nl <- lapply(depth_tags, function(dd) lapply(time_tags, function(tt) rm_leg(all_plots[[paste0(date_tag, "_", tt, "_COTS outbreak initiation zone_", dd)]])))
figure_depth_by_time <- cowplot::plot_grid(plotlist = unlist(rows_nl, recursive = FALSE),
                                           ncol = length(time_tags), align = "hv", axis = "tblr")

# ---- Panel aspect ----
panel_aspect <- diff(ylim) / (diff(xlim) * cos(pi * mean(ylim) / 180))
panel_w <- 4
panel_h <- panel_w * panel_aspect

ncol <- 2
nrow <- length(depth_tags)

out_file <- file.path(
  '[insert filepath]',
  paste0(date_tag, "_", ".png")
)

ggsave(
  filename  = out_file,
  plot      = figure_depth_by_time,
  width     = ncol * panel_w,
  height    = nrow * panel_h,
  dpi       = 300,
  limitsize = FALSE,
  bg        = "white"
)

# Open externally to avoid RStudio's Web/Plotly viewer
invisible(try(utils::browseURL(out_file), silent = TRUE))
cat("Plot saved to:", out_file, "\n")
```

#### Russel-Mulgrave-Townsville
```{r}
# ===================
# Supplementary Fig 1
# ===================

# ---- Turn off s2 early (re-enable on exit) ----
old_s2 <- sf_use_s2(FALSE)
on.exit(sf_use_s2(old_s2), add = TRUE)

# ---- Date tag (single source of truth) ----
date_tag <- "2011-02-22"

# ---- Static window ----
xlim <- c(145.7, 147.257161)
ylim <- c(-19.423058, -16.8)
bbox <- st_bbox(c(xmin = xlim[1], ymin = ylim[1], xmax = xlim[2], ymax = ylim[2]), crs = st_crs(4326))

# ---- Legend remover ----
rm_leg <- function(p) p + theme(legend.position = "none") +
  guides(colour = "none", color = "none", fill = "none")

# ---- Land (QLD) ----
states <- rnaturalearth::ne_states(country = "Australia", returnclass = "sf")
qld <- states[grepl("Queensland", states$name, TRUE) |
              (("name_en" %in% names(states)) & grepl("Queensland", states$name_en, TRUE)), ] |>
  st_crop(bbox)

# ---- Reefs ----
use_cache  <- TRUE
cache_file <- file.path(tempdir(), sprintf("gbr_features_%0.1f-%0.1f_%0.1f-%0.1f.rds", xlim[1], xlim[2], ylim[1], ylim[2]))

if (use_cache && file.exists(cache_file)) {
  gbr <- readRDS(cache_file)
} else {
  wfs_base <- "https://maps.eatlas.org.au/maps/wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=ea:GBR_GBRMPA_GBR-features&outputFormat=application/json&srsName=EPSG:4326"
  bbox_q   <- sprintf("&bbox=%f,%f,%f,%f,EPSG:4326", xlim[1], ylim[1], xlim[2], ylim[2])
  gbr_raw <- tryCatch(
    st_read(paste0(wfs_base, bbox_q), quiet = TRUE),
    error = function(e) st_read(wfs_base, quiet = TRUE)
  )
  gbr_fix <- suppressWarnings(
    if ("st_make_valid" %in% ls("package:sf")) sf::st_make_valid(gbr_raw) else st_buffer(gbr_raw, 0)
  )
  gbr <- gbr_fix |> st_crop(bbox) |> st_collection_extract("POLYGON", warn = FALSE)
  if (use_cache) { dir.create(dirname(cache_file), showWarnings = FALSE, recursive = TRUE); saveRDS(gbr, cache_file) }
}

# ---- Palette & transform ----
salinity_cols <- colorRampPalette(c(rgb(48,18,59, maxColorValue = 255), "red","orange","yellow","cyan","blue"))
sal_stretch_trans <- scales::trans_new(
  "sal_stretch",
  transform = function(x) ifelse(x <= 30, x * (0.8/30), 0.8 + (x - 30) * (0.2/6)),
  inverse   = function(y) ifelse(y <= 0.8, y * (30/0.8), 30 + (y - 0.8) * (6/0.2))
)

# ---- Open NetCDF ----
ncfile_alts <- c(
  sprintf("https://thredds.nci.org.au/thredds/dodsC/fx3/gbr4_H4p0_ABARRAr2_OBRAN2020_FG2Gv3_Dhnd/gbr4_simple_%s.nc", date_tag)
)

open_nc_safely <- function(urls) {
  for (u in urls) {
    con <- try(open.nc(u), silent = TRUE)
    if (!inherits(con, "try-error")) return(con)
  }
  stop("All OPeNDAP endpoints failed to open.")
}
nc <- open_nc_safely(ncfile_alts); on.exit(close.nc(nc), add = TRUE)

lon <- var.get.nc(nc, "longitude")
lat <- var.get.nc(nc, "latitude")
zc  <- var.get.nc(nc, "zc")

# ---- Safe getter ----
safe_var_get <- function(nc, var, start, count, retries = 6, base_wait = 1.5) {
  for (i in seq_len(retries)) {
    out <- try(var.get.nc(nc, var, start = start, count = count), silent = TRUE)
    if (!inherits(out, "try-error")) return(out)
    Sys.sleep(base_wait * (2^(i - 1)))
  }
  stop("OPeNDAP read failed after retries.")
}

# ---- Map builder ----
make_map <- function(depth_m, time_idx) {
  k_idx <- which.min(abs(zc - (-depth_m)))
  salt  <- safe_var_get(nc, "salt",
                        start = c(1, 1, k_idx, time_idx),
                        count = c(dim(lon)[1], dim(lon)[2], 1, 1))

  df <- data.frame(lon = as.vector(lon),
                   lat = as.vector(lat),
                   salt = as.vector(salt)) |>
        dplyr::filter(!is.na(salt) & salt < 1e30,
                      dplyr::between(lat, ylim[1], ylim[2]),
                      dplyr::between(lon, xlim[1], xlim[2]))

  ggplot() +
    geom_sf(data = qld, fill = "#f6f2ee", color = NA) +
    geom_point(
      data = df,
      mapping = aes(x = lon, y = lat, color = pmin(pmax(salt, 0), 36)),
      shape = 15, size = 5.5, stroke = 0
    ) +
    scale_color_gradientn(
      colours = salinity_cols(256),
      limits  = c(0, 38),
      oob     = scales::squish,
      name    = "Salinity (PSU)",
      breaks  = seq(0, 38, by = 5),
      trans   = sal_stretch_trans
    ) +
    geom_sf(data = gbr, fill = NA, color = "black", size = 0.2, inherit.aes = FALSE) +
    coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
    scale_x_continuous(breaks = seq(146, 147, 1)) +
    scale_y_continuous(breaks = seq(-19, -17, 1)) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text  = element_text(size = 22),
      axis.ticks = element_line(color = "black"),
      plot.margin = margin(0, 0, 0, 0)
    )
}

# ---- Depths & times ----
depths      <- c(0.5, 3.00, 5.55, 8.80, 12.75)
depth_tags  <- c("0p5","3p00","5p55","8p80","12p75")
time_tags   <- c("0000","1200")
time_map    <- c("0000" = 1, "1200" = 13)

# ---- Generate plots ----
all_plots <- setNames(vector("list", length(time_tags) * length(depth_tags)),
  as.vector(outer(time_tags, depth_tags, function(tt, dd) paste0(date_tag, "_", tt, "_Russel-Mulgrave-Townsville_", dd))))

for (ttag in names(time_map)) {
  t_idx <- time_map[[ttag]]
  panels <- setNames(
    mapply(function(d, dtag) make_map(d, t_idx), depths, depth_tags, SIMPLIFY = FALSE),
    paste0(date_tag, "_", ttag, "_Normanby_", depth_tags)
  )
  all_plots[names(panels)] <- panels
}

rows_nl <- lapply(depth_tags, function(dd) lapply(time_tags, function(tt) rm_leg(all_plots[[paste0(date_tag, "_", tt, "_Russel-Mulgrave-Townsville_", dd)]])))
figure_depth_by_time <- cowplot::plot_grid(plotlist = unlist(rows_nl, recursive = FALSE),
                                           ncol = length(time_tags), align = "hv", axis = "tblr")

# ---- Panel aspect ----
panel_aspect <- diff(ylim) / (diff(xlim) * cos(pi * mean(ylim) / 180))
panel_w <- 4
panel_h <- panel_w * panel_aspect

ncol <- 2
nrow <- length(depth_tags)

out_file <- file.path(
  '[insert filepath]',
  paste0(date_tag, "_", ".png")
)

ggsave(
  filename  = out_file,
  plot      = figure_depth_by_time,
  width     = ncol * panel_w,
  height    = nrow * panel_h,
  dpi       = 300,
  limitsize = FALSE,
  bg        = "white"
)

# Open externally to avoid RStudio's Web/Plotly viewer
invisible(try(utils::browseURL(out_file), silent = TRUE))
cat("Plot saved to:", out_file, "\n")
```

### 2019
```{r}
#Notes: The code presented provides an example for generating outputs for the first day of the modeled time series. For the 2019 flood event, plume dynamics were mapped for the following dates: 28 January, 7, 10, 11, 14, and 15 February. The same code can be copied and applied to eReefs datasets representing each of these dates to generate the corresponding plume visualisations and analyses.
```

#### COTS outbreak initiation zone
```{r}
# ====================
# Supplementary Fig. 2
# ====================

# ---- Turn off s2 early (re-enable on exit) ----
old_s2 <- sf_use_s2(FALSE)
on.exit(sf_use_s2(old_s2), add = TRUE)

# ---- Static window ----
xlim <- c(145, 147) #update
ylim <- c(-17.5, -14.5) #update
bbox <- st_bbox(c(xmin = xlim[1], ymin = ylim[1], xmax = xlim[2], ymax = ylim[2]), crs = st_crs(4326)) #update

# ---- Legend remover ----
rm_leg <- function(p) p + theme(legend.position = "none") +
  guides(colour = "none", color = "none", fill = "none")

# ---- Land (QLD) ----
states <- rnaturalearth::ne_states(country = "Australia", returnclass = "sf")
qld <- states[grepl("Queensland", states$name, TRUE) |
              (("name_en" %in% names(states)) & grepl("Queensland", states$name_en, TRUE)), ] |>
  st_crop(bbox)

# ---- Reefs ----
use_cache  <- TRUE #update
cache_file <- file.path(tempdir(), sprintf("gbr_features_%0.1f-%0.1f_%0.1f-%0.1f.rds", xlim[1], xlim[2], ylim[1], ylim[2])) #update


if (use_cache && file.exists(cache_file)) {
  gbr <- readRDS(cache_file)
} else {
  wfs_base <- "https://maps.eatlas.org.au/maps/wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=ea:GBR_GBRMPA_GBR-features&outputFormat=application/json&srsName=EPSG:4326"
  bbox_q   <- sprintf("&bbox=%f,%f,%f,%f,EPSG:4326", xlim[1], ylim[1], xlim[2], ylim[2])
  gbr_raw <- tryCatch(
    st_read(paste0(wfs_base, bbox_q), quiet = TRUE),
    error = function(e) st_read(wfs_base, quiet = TRUE)
  )
  gbr_fix <- suppressWarnings(
    if ("st_make_valid" %in% ls("package:sf")) sf::st_make_valid(gbr_raw) else st_buffer(gbr_raw, 0)
  )
  gbr <- gbr_fix |> st_crop(bbox) |> st_collection_extract("POLYGON", warn = FALSE)
  if (use_cache) { dir.create(dirname(cache_file), showWarnings = FALSE, recursive = TRUE); saveRDS(gbr, cache_file) }
}

# ---- Palette & transform ----
salinity_cols <- colorRampPalette(c(rgb(48,18,59, maxColorValue = 255), "red","orange","yellow","cyan","blue"))
sal_stretch_trans <- scales::trans_new(
  "sal_stretch",
  transform = function(x) ifelse(x <= 30, x * (0.8/30), 0.8 + (x - 30) * (0.2/6)),
  inverse   = function(y) ifelse(y <= 0.8, y * (30/0.8), 30 + (y - 0.8) * (6/0.2))
)

# ---- Open NetCDF ----
ncfile_alts <- c(
  "https://dapds00.nci.org.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2019-01-28.nc",
  "https://thredds.ereefs.aims.gov.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2019-01-28.nc"
)

open_nc_safely <- function(urls) {
  for (u in urls) {
    con <- try(open.nc(u), silent = TRUE)
    if (!inherits(con, "try-error")) return(con)
  }
  stop("All OPeNDAP endpoints failed to open.")
}
nc <- open_nc_safely(ncfile_alts); on.exit(close.nc(nc), add = TRUE)

lon <- var.get.nc(nc, "longitude")
lat <- var.get.nc(nc, "latitude")
zc  <- var.get.nc(nc, "zc")

# ---- Safe getter ----
safe_var_get <- function(nc, var, start, count, retries = 6, base_wait = 1.5) {
  for (i in seq_len(retries)) {
    out <- try(var.get.nc(nc, var, start = start, count = count), silent = TRUE)
    if (!inherits(out, "try-error")) return(out)
    Sys.sleep(base_wait * (2^(i - 1)))
  }
  stop("OPeNDAP read failed after retries.")
}

# ---- Map builder ----
make_map <- function(depth_m, time_idx) {
  k_idx <- which.min(abs(zc - (-depth_m)))
  salt  <- safe_var_get(nc, "salt",
                        start = c(1, 1, k_idx, time_idx),
                        count = c(dim(lon)[1], dim(lon)[2], 1, 1))

  df <- data.frame(lon = as.vector(lon),
                   lat = as.vector(lat),
                   salt = as.vector(salt)) |>
        dplyr::filter(!is.na(salt) & salt < 1e30,
                      dplyr::between(lat, ylim[1], ylim[2]),
                      dplyr::between(lon, xlim[1], xlim[2]))

  pts_sf <- st_as_sf(df, coords = c("lon","lat"), crs = 4326)

  ggplot() +
    geom_sf(data = qld, fill = "#f6f2ee", color = NA) +
    geom_sf(data = pts_sf, aes(color = pmin(pmax(salt, 0), 36)), size = 0.2) +
    scale_color_gradientn(
      colours = salinity_cols(256),
      limits  = c(0, 38),
      oob     = scales::squish,
      name    = "Salinity (PSU)",
      breaks  = seq(0, 38, by = 5),
      trans   = sal_stretch_trans
    ) +
    geom_sf(data = gbr, fill = NA, color = "black", size = 0.2, inherit.aes = FALSE) +
    coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
   scale_x_continuous(breaks = seq(145.5, 146.5, 1)) +
   scale_y_continuous(breaks = seq(-17, -15, 1)) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text  = element_text(size = 22),
      axis.ticks = element_line(color = "black")
    )
}

# ---- Depths & times ----
depths      <- c(0.5, 2.35, 5.35, 9.00, 13.00)
depth_tags  <- c("0p5","2p35","5p35","9p00","13p00")
time_tags   <- c("0000","1200")
time_map    <- c("0000" = 1, "1200" = 13)

# ---- Generate plots ----
all_plots <- setNames(vector("list", length(time_tags) * length(depth_tags)),
                      as.vector(outer(time_tags, depth_tags, function(tt, dd) paste0("Dec-March 2019", tt, "_initiationzone_", dd))))

for (ttag in names(time_map)) {
  t_idx <- time_map[[ttag]]
  panels <- setNames(
    mapply(function(d, dtag) make_map(d, t_idx), depths, depth_tags, SIMPLIFY = FALSE),
    paste0("Dec-March 2019", ttag, "_initiationzone_", depth_tags)
  )
  all_plots[names(panels)] <- panels
}

rows_nl <- lapply(depth_tags, function(dd) lapply(time_tags, function(tt) rm_leg(all_plots[[paste0("Dec-March 2019", tt, "_initiationzone_", dd)]])))
figure_depth_by_time <- cowplot::plot_grid(plotlist = unlist(rows_nl, recursive = FALSE),
                                           ncol = length(time_tags), align = "hv", axis = "tblr")

# ---- Panel aspect ----
panel_aspect <- diff(ylim) / (diff(xlim) * cos(pi * mean(ylim) / 180))
panel_w <- 4
panel_h <- panel_w * panel_aspect

ncol <- 2
nrow <- length(depth_tags)

# ---- Save ----
out_dir  <- '[insert filepath]' #update
out_file <- file.path(out_dir, "28012019_0p5to13m.png")

ggsave(
  filename  = out_file,
  plot      = figure_depth_by_time,
  width     = ncol * panel_w,
  height    = nrow * panel_h,
  dpi       = 300,
  limitsize = FALSE
)
```

#### Russel-Mulgrave-Townsville
```{r}
# ====================
# Supplementary Fig. 2
# ====================

# ---- Turn off s2 early (re-enable on exit) ----
old_s2 <- sf_use_s2(FALSE)
on.exit(sf_use_s2(old_s2), add = TRUE)

# ---- Static window ----
xlim <- c(145.967197, 147.257161) #update
ylim <- c(-19.423058, -17.224817) #update
bbox <- st_bbox(c(xmin = xlim[1], ymin = ylim[1], xmax = xlim[2], ymax = ylim[2]), crs = st_crs(4326)) #update

# ---- Legend remover ----
rm_leg <- function(p) p + theme(legend.position = "none") +
  guides(colour = "none", color = "none", fill = "none")

# ---- Land (QLD) ----
states <- rnaturalearth::ne_states(country = "Australia", returnclass = "sf")
qld <- states[grepl("Queensland", states$name, TRUE) |
              (("name_en" %in% names(states)) & grepl("Queensland", states$name_en, TRUE)), ] |>
  st_crop(bbox)

# ---- Reefs ----
use_cache  <- TRUE #update
cache_file <- file.path(tempdir(), sprintf("gbr_features_%0.1f-%0.1f_%0.1f-%0.1f.rds", xlim[1], xlim[2], ylim[1], ylim[2])) #update


if (use_cache && file.exists(cache_file)) {
  gbr <- readRDS(cache_file)
} else {
  wfs_base <- "https://maps.eatlas.org.au/maps/wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=ea:GBR_GBRMPA_GBR-features&outputFormat=application/json&srsName=EPSG:4326"
  bbox_q   <- sprintf("&bbox=%f,%f,%f,%f,EPSG:4326", xlim[1], ylim[1], xlim[2], ylim[2])
  gbr_raw <- tryCatch(
    st_read(paste0(wfs_base, bbox_q), quiet = TRUE),
    error = function(e) st_read(wfs_base, quiet = TRUE)
  )
  gbr_fix <- suppressWarnings(
    if ("st_make_valid" %in% ls("package:sf")) sf::st_make_valid(gbr_raw) else st_buffer(gbr_raw, 0)
  )
  gbr <- gbr_fix |> st_crop(bbox) |> st_collection_extract("POLYGON", warn = FALSE)
  if (use_cache) { dir.create(dirname(cache_file), showWarnings = FALSE, recursive = TRUE); saveRDS(gbr, cache_file) }
}

# ---- Palette & transform ----
salinity_cols <- colorRampPalette(c(rgb(48,18,59, maxColorValue = 255), "red","orange","yellow","cyan","blue"))
sal_stretch_trans <- scales::trans_new(
  "sal_stretch",
  transform = function(x) ifelse(x <= 30, x * (0.8/30), 0.8 + (x - 30) * (0.2/6)),
  inverse   = function(y) ifelse(y <= 0.8, y * (30/0.8), 30 + (y - 0.8) * (6/0.2))
)

# ---- Open NetCDF ----
ncfile_alts <- c(
  "https://dapds00.nci.org.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2019-01-28.nc",
  "https://thredds.ereefs.aims.gov.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2019-01-28.nc"
)

open_nc_safely <- function(urls) {
  for (u in urls) {
    con <- try(open.nc(u), silent = TRUE)
    if (!inherits(con, "try-error")) return(con)
  }
  stop("All OPeNDAP endpoints failed to open.")
}
nc <- open_nc_safely(ncfile_alts); on.exit(close.nc(nc), add = TRUE)

lon <- var.get.nc(nc, "longitude")
lat <- var.get.nc(nc, "latitude")
zc  <- var.get.nc(nc, "zc")

# ---- Safe getter ----
safe_var_get <- function(nc, var, start, count, retries = 6, base_wait = 1.5) {
  for (i in seq_len(retries)) {
    out <- try(var.get.nc(nc, var, start = start, count = count), silent = TRUE)
    if (!inherits(out, "try-error")) return(out)
    Sys.sleep(base_wait * (2^(i - 1)))
  }
  stop("OPeNDAP read failed after retries.")
}

# ---- Map builder ----
make_map <- function(depth_m, time_idx) {
  k_idx <- which.min(abs(zc - (-depth_m)))
  salt  <- safe_var_get(nc, "salt",
                        start = c(1, 1, k_idx, time_idx),
                        count = c(dim(lon)[1], dim(lon)[2], 1, 1))

  df <- data.frame(lon = as.vector(lon),
                   lat = as.vector(lat),
                   salt = as.vector(salt)) |>
        dplyr::filter(!is.na(salt) & salt < 1e30,
                      dplyr::between(lat, ylim[1], ylim[2]),
                      dplyr::between(lon, xlim[1], xlim[2]))

  pts_sf <- st_as_sf(df, coords = c("lon","lat"), crs = 4326)

  ggplot() +
    geom_sf(data = qld, fill = "#f6f2ee", color = NA) +
    geom_sf(data = pts_sf, aes(color = pmin(pmax(salt, 0), 36)), size = 0.2) +
    scale_color_gradientn(
      colours = salinity_cols(256),
      limits  = c(0, 38),
      oob     = scales::squish,
      name    = "Salinity (PSU)",
      breaks  = seq(0, 38, by = 5),
      trans   = sal_stretch_trans
    ) +
    geom_sf(data = gbr, fill = NA, color = "black", size = 0.2, inherit.aes = FALSE) +
    coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
    scale_x_continuous(breaks = seq(145, 147, 1)) +
   scale_y_continuous(breaks = seq(-19, -17, 1)) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text  = element_text(size = 22),
      axis.ticks = element_line(color = "black")
    )
}

# ---- Depths & times ----
depths      <- c(0.5, 2.35, 5.35, 9.00, 13.00)
depth_tags  <- c("0p5","2p35","5p35","9p00","13p00")
time_tags   <- c("0000","1200")
time_map    <- c("0000" = 1, "1200" = 13)

# ---- Generate plots ----
all_plots <- setNames(vector("list", length(time_tags) * length(depth_tags)),
                      as.vector(outer(time_tags, depth_tags, function(tt, dd) paste0("Dec-March 2019", tt, "MulgraveRiver_Twnsville", dd))))

for (ttag in names(time_map)) {
  t_idx <- time_map[[ttag]]
  panels <- setNames(
    mapply(function(d, dtag) make_map(d, t_idx), depths, depth_tags, SIMPLIFY = FALSE),
    paste0("Dec-March 2019", ttag, "MulgraveRiver_Twnsville", depth_tags)
  )
  all_plots[names(panels)] <- panels
}

rows_nl <- lapply(depth_tags, function(dd) lapply(time_tags, function(tt) rm_leg(all_plots[[paste0("Dec-March 2019", tt, "MulgraveRiver_Twnsville", dd)]])))
figure_depth_by_time <- cowplot::plot_grid(plotlist = unlist(rows_nl, recursive = FALSE),
                                           ncol = length(time_tags), align = "hv", axis = "tblr")

# ---- Panel aspect ----
panel_aspect <- diff(ylim) / (diff(xlim) * cos(pi * mean(ylim) / 180))
panel_w <- 4
panel_h <- panel_w * panel_aspect

ncol <- 2
nrow <- length(depth_tags)

# ---- Save ----
out_dir  <- '[insert filepath]' #update
out_file <- file.path(out_dir, "28012019_0p5to13m.png")

ggsave(
  filename  = out_file,
  plot      = figure_depth_by_time,
  width     = ncol * panel_w,
  height    = nrow * panel_h,
  dpi       = 300,
  limitsize = FALSE
)
```

#### Burdekin
```{r}
# ====================
# Supplementary Fig. 2
# ====================

# ---- Turn off s2 early (re-enable on exit) ----
old_s2 <- sf_use_s2(FALSE)
on.exit(sf_use_s2(old_s2), add = TRUE)

# ---- Static window ----
xlim <- c(147, 149) #update
ylim <- c(-20.532210,-18.423058) #update
# Build bbox directly from limits in WGS84 so cropping/scales use one CRS consistently
bbox <- st_as_sfc(st_bbox(c(xmin = xlim[1], xmax = xlim[2], ymin = ylim[1], ymax = ylim[2]), crs = 4326))

#-20.621660, 148.729377
#xlim <- c(145.967197, 147.257161) #update
#ylim <- c(-19.423058, -17.224817) #update
#-20.532210, 148.932141

# ---- Legend remover ----
rm_leg <- function(p) p + theme(legend.position = "none") +
  guides(color = "none", fill = "none")

# ---- Land (QLD) ----
states <- rnaturalearth::ne_states(country = "Australia", returnclass = "sf")
qld <- states[grepl("Queensland", states$name, TRUE) |
              (("name_en" %in% names(states)) & grepl("Queensland", states$name_en, TRUE)), ] |>
  st_crop(bbox)

# ---- Reefs ----
use_cache  <- TRUE #update
cache_file <- file.path(tempdir(), sprintf("gbr_features_%0.1f-%0.1f_%0.1f-%0.1f.rds", xlim[1], xlim[2], ylim[1], ylim[2])) #update


if (use_cache && file.exists(cache_file)) {
  gbr <- readRDS(cache_file)
} else {
  wfs_base <- "https://maps.eatlas.org.au/maps/wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=ea:GBR_GBRMPA_GBR-features&outputFormat=application/json&srsName=EPSG:4326"
  bbox_q   <- sprintf("&bbox=%f,%f,%f,%f,EPSG:4326", xlim[1], ylim[1], xlim[2], ylim[2])
  gbr_raw <- tryCatch(
    st_read(paste0(wfs_base, bbox_q), quiet = TRUE),
    error = function(e) st_read(wfs_base, quiet = TRUE)
  )
  gbr_fix <- suppressWarnings(
    if ("st_make_valid" %in% ls("package:sf")) sf::st_make_valid(gbr_raw) else st_buffer(gbr_raw, 0)
  )
  gbr <- gbr_fix |> st_crop(bbox) |> st_collection_extract("POLYGON", warn = FALSE)
  if (use_cache) { dir.create(dirname(cache_file), showWarnings = FALSE, recursive = TRUE); saveRDS(gbr, cache_file) }
}

# ---- Palette & transform ----
salinity_cols <- colorRampPalette(c(rgb(48,18,59, maxColorValue = 255), "red","orange","yellow","cyan","blue"))
sal_stretch_trans <- scales::trans_new(
  "sal_stretch",
  transform = function(x) ifelse(x <= 30, x * (0.8/30), 0.8 + (x - 30) * (0.2/6)),
  inverse   = function(y) ifelse(y <= 0.8, y * (30/0.8), 30 + (y - 0.8) * (6/0.2))
)

# ---- Open NetCDF ----
ncfile_alts <- c(
  "https://dapds00.nci.org.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2019-01-28.nc",
  "https://thredds.ereefs.aims.gov.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2019-01-28.nc"
)

open_nc_safely <- function(urls) {
  for (u in urls) {
    con <- try(open.nc(u), silent = TRUE)
    if (!inherits(con, "try-error")) return(con)
  }
  stop("All OPeNDAP endpoints failed to open.")
}
nc <- open_nc_safely(ncfile_alts); on.exit(close.nc(nc), add = TRUE)

lon <- var.get.nc(nc, "longitude")
lat <- var.get.nc(nc, "latitude")
zc  <- var.get.nc(nc, "zc")

# ---- Safe getter ----
safe_var_get <- function(nc, var, start, count, retries = 6, base_wait = 1.5) {
  for (i in seq_len(retries)) {
    out <- try(var.get.nc(nc, var, start = start, count = count), silent = TRUE)
    if (!inherits(out, "try-error")) return(out)
    Sys.sleep(base_wait * (2^(i - 1)))
  }
  stop("OPeNDAP read failed after retries.")
}

# ---- Map builder ----
make_map <- function(depth_m, time_idx) {
  k_idx <- which.min(abs(zc - (-depth_m)))
  salt  <- safe_var_get(nc, "salt",
                        start = c(1, 1, k_idx, time_idx),
                        count = c(dim(lon)[1], dim(lon)[2], 1, 1))

  df <- data.frame(lon = as.vector(lon),
                   lat = as.vector(lat),
                   salt = as.vector(salt)) |>
        dplyr::filter(!is.na(salt) & salt < 1e30,
                      dplyr::between(lat, ylim[1], ylim[2]),
                      dplyr::between(lon, xlim[1], xlim[2]))

  pts_sf <- st_as_sf(df, coords = c("lon","lat"), crs = 4326)

  ggplot() +
    geom_sf(data = qld, fill = "#f6f2ee", color = NA) +
    geom_sf(data = pts_sf, aes(color = pmin(pmax(salt, 0), 36)), size = 0.2) +
    scale_color_gradientn(
      colours = salinity_cols(256),
      limits  = c(0, 38),
      oob     = scales::squish,
      name    = "Salinity (PSU)",
      breaks  = seq(0, 38, by = 5),
      trans   = sal_stretch_trans
    ) +
    geom_sf(data = gbr, fill = NA, color = "black", size = 0.2, inherit.aes = FALSE) +
    coord_sf(expand = FALSE) +
    scale_x_continuous(limits = xlim, breaks = seq(147, 149, 2)) +
    scale_y_continuous(limits = ylim, breaks = seq(-20, -19, 1)) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text  = element_text(size = 22),
      axis.ticks = element_line(color = "black")
    )
}

# ---- Depths & times ----
depths      <- c(0.5, 2.35, 5.35, 9.00, 13.00)
depth_tags  <- c("0p5","2p35","5p35","9p00","13p00")
time_tags   <- c("0000","1200")
time_map    <- c("0000" = 1, "1200" = 13)

# ---- Generate plots ----
all_plots <- setNames(vector("list", length(time_tags) * length(depth_tags)),
                      as.vector(outer(time_tags, depth_tags, function(tt, dd) paste0("Dec-March 2019", tt, "Burdekin", dd))))

for (ttag in names(time_map)) {
  t_idx <- time_map[[ttag]]
  panels <- setNames(
    mapply(function(d, dtag) make_map(d, t_idx), depths, depth_tags, SIMPLIFY = FALSE),
    paste0("Dec-March 2019", ttag, "Burdekin", depth_tags)
  )
  all_plots[names(panels)] <- panels
}

rows_nl <- lapply(depth_tags, function(dd) lapply(time_tags, function(tt) rm_leg(all_plots[[paste0("Dec-March 2019", tt, "Burdekin", dd)]])))
figure_depth_by_time <- cowplot::plot_grid(plotlist = unlist(rows_nl, recursive = FALSE),
                                           ncol = length(time_tags), align = "hv", axis = "tblr")

# ---- Panel aspect ----
panel_aspect <- diff(ylim) / (diff(xlim) * cos(pi * mean(ylim) / 180))
panel_w <- 4
panel_h <- panel_w * panel_aspect

ncol <- 2
nrow <- length(depth_tags)

# ---- Save ----
out_dir  <- '[insert filepath]' #update
out_file <- file.path(out_dir, "28Jan19_0p5to13m.png")

ggsave(
  filename  = out_file,
  plot      = figure_depth_by_time,
  width     = ncol * panel_w,
  height    = nrow * panel_h,
  dpi       = 300,
  limitsize = FALSE
)
```

### 2023
```{r}
#Notes: The code presented provides an example for generating outputs for the first day of the modeled time series. For the 2023 flood event, plume dynamics were mapped for the following dates: 18, 21, 22, 23, 24 and 26 December. The same code can be copied and applied to eReefs datasets representing each of these dates to generate the corresponding plume visualisations and analyses.
```

#### COTS Outbreak Initiation Zone
```{r}
# ====================
# Supplementary Fig. 3
# ====================

# ---- Turn off s2 early (re-enable on exit) ----
old_s2 <- sf_use_s2(FALSE)
on.exit(sf_use_s2(old_s2), add = TRUE)

# ---- Static window ----
xlim <- c(144.7, 146.5)
ylim <- c(-17.5, -14.5)
bbox <- st_bbox(c(xmin = xlim[1], ymin = ylim[1], xmax = xlim[2], ymax = ylim[2]), crs = st_crs(4326))

# ---- Legend remover ----
rm_leg <- function(p) p + theme(legend.position = "none") +
  guides(colour = "none", color = "none", fill = "none")

# ---- Land (QLD) ----
states <- rnaturalearth::ne_states(country = "Australia", returnclass = "sf")
qld <- states[grepl("Queensland", states$name, TRUE) |
              (("name_en" %in% names(states)) & grepl("Queensland", states$name_en, TRUE)), ] |>
  st_crop(bbox)

# ---- Reefs ----
use_cache  <- TRUE
cache_file <- file.path(tempdir(), sprintf("gbr_features_%0.1f-%0.1f_%0.1f-%0.1f.rds", xlim[1], xlim[2], ylim[1], ylim[2]))

if (use_cache && file.exists(cache_file)) {
  gbr <- readRDS(cache_file)
} else {
  wfs_base <- "https://maps.eatlas.org.au/maps/wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=ea:GBR_GBRMPA_GBR-features&outputFormat=application/json&srsName=EPSG:4326"
  bbox_q   <- sprintf("&bbox=%f,%f,%f,%f,EPSG:4326", xlim[1], ylim[1], xlim[2], ylim[2])
  gbr_raw <- tryCatch(
    st_read(paste0(wfs_base, bbox_q), quiet = TRUE),
    error = function(e) st_read(wfs_base, quiet = TRUE)
  )
  gbr_fix <- suppressWarnings(
    if ("st_make_valid" %in% ls("package:sf")) sf::st_make_valid(gbr_raw) else st_buffer(gbr_raw, 0)
  )
  gbr <- gbr_fix |> st_crop(bbox) |> st_collection_extract("POLYGON", warn = FALSE)
  if (use_cache) { dir.create(dirname(cache_file), showWarnings = FALSE, recursive = TRUE); saveRDS(gbr, cache_file) }
}

# ---- Palette & transform ----
salinity_cols <- colorRampPalette(c(rgb(48,18,59, maxColorValue = 255), "red","orange","yellow","cyan","blue"))
sal_stretch_trans <- scales::trans_new(
  "sal_stretch",
  transform = function(x) ifelse(x <= 30, x * (0.8/30), 0.8 + (x - 30) * (0.2/6)),
  inverse   = function(y) ifelse(y <= 0.8, y * (30/0.8), 30 + (y - 0.8) * (6/0.2))
)

# ---- Open NetCDF ----
ncfile_alts <- c(
  "https://dapds00.nci.org.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2023-12-18.nc",
  "https://thredds.ereefs.aims.gov.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2023-12-18.nc"
)

open_nc_safely <- function(urls) {
  for (u in urls) {
    con <- try(open.nc(u), silent = TRUE)
    if (!inherits(con, "try-error")) return(con)
  }
  stop("All OPeNDAP endpoints failed to open.")
}
nc <- open_nc_safely(ncfile_alts); on.exit(close.nc(nc), add = TRUE)

lon <- var.get.nc(nc, "longitude")
lat <- var.get.nc(nc, "latitude")
zc  <- var.get.nc(nc, "zc")

# ---- Safe getter ----
safe_var_get <- function(nc, var, start, count, retries = 6, base_wait = 1.5) {
  for (i in seq_len(retries)) {
    out <- try(var.get.nc(nc, var, start = start, count = count), silent = TRUE)
    if (!inherits(out, "try-error")) return(out)
    Sys.sleep(base_wait * (2^(i - 1)))
  }
  stop("OPeNDAP read failed after retries.")
}

# ---- Map builder ----
make_map <- function(depth_m, time_idx) {
  k_idx <- which.min(abs(zc - (-depth_m)))
  salt  <- safe_var_get(nc, "salt",
                        start = c(1, 1, k_idx, time_idx),
                        count = c(dim(lon)[1], dim(lon)[2], 1, 1))

  df <- data.frame(lon = as.vector(lon),
                   lat = as.vector(lat),
                   salt = as.vector(salt)) |>
        dplyr::filter(!is.na(salt) & salt < 1e30,
                      dplyr::between(lat, ylim[1], ylim[2]),
                      dplyr::between(lon, xlim[1], xlim[2]))

  pts_sf <- st_as_sf(df, coords = c("lon","lat"), crs = 4326)

  ggplot() +
    geom_sf(data = qld, fill = "#f6f2ee", color = NA) +
    geom_sf(data = pts_sf, aes(color = pmin(pmax(salt, 0), 36)), size = 0.2) +
    scale_color_gradientn(
      colours = salinity_cols(256),
      limits  = c(0, 38),
      oob     = scales::squish,
      name    = "Salinity (PSU)",
      breaks  = seq(0, 38, by = 5),
      trans   = sal_stretch_trans
    ) +
    geom_sf(data = gbr, fill = NA, color = "black", size = 0.2, inherit.aes = FALSE) +
    coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
    scale_x_continuous(breaks = seq(145, 146.5, 1)) +
    scale_y_continuous(breaks = seq(-17.5, -14.5, 3)) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text  = element_text(size = 22),
      axis.ticks = element_line(color = "black")
    )
}

# ---- Depths & times ----
depths      <- c(0.5, 2.35, 5.35, 9.00, 13.00)
depth_tags  <- c("0p5","2p35","5p35","9p00","13p00")
time_tags   <- c("0000","1200")
time_map    <- c("0000" = 1, "1200" = 13)

# ---- Generate plots ----
all_plots <- setNames(vector("list", length(time_tags) * length(depth_tags)),
                      as.vector(outer(time_tags, depth_tags, function(tt, dd) paste0("Dec182023_", tt, "_initiationzone_", dd))))

for (ttag in names(time_map)) {
  t_idx <- time_map[[ttag]]
  panels <- setNames(
    mapply(function(d, dtag) make_map(d, t_idx), depths, depth_tags, SIMPLIFY = FALSE),
    paste0("Dec182023_", ttag, "_initiationzone_", depth_tags)
  )
  all_plots[names(panels)] <- panels
}

rows_nl <- lapply(depth_tags, function(dd) lapply(time_tags, function(tt) rm_leg(all_plots[[paste0("Dec182023_", tt, "_initiationzone_", dd)]])))
figure_depth_by_time <- cowplot::plot_grid(plotlist = unlist(rows_nl, recursive = FALSE),
                                           ncol = length(time_tags), align = "hv", axis = "tblr")

# ---- Panel aspect ----
panel_aspect <- diff(ylim) / (diff(xlim) * cos(pi * mean(ylim) / 180))
panel_w <- 4
panel_h <- panel_w * panel_aspect

ncol <- 2
nrow <- length(depth_tags)

# ---- Save ----
out_dir  <- '[insert filepath]'
out_file <- file.path(out_dir, "18Dec23_0p5to13m.png")

ggsave(
  filename  = out_file,
  plot      = figure_depth_by_time,
  width     = ncol * panel_w,
  height    = nrow * panel_h,
  dpi       = 300,
  limitsize = FALSE
)
```

#### Russel-Mulgrave-Townsville
```{r}
# ====================
# Supplementary Fig. 3
# ====================

# ---- Turn off s2 early (re-enable on exit) ----
old_s2 <- sf_use_s2(FALSE)
on.exit(sf_use_s2(old_s2), add = TRUE)

# ---- Static window ----
xlim <- c(145.7, 147.257161)
ylim <- c(-19.423058, -16.8)
bbox <- st_bbox(c(xmin = xlim[1], ymin = ylim[1], xmax = xlim[2], ymax = ylim[2]), crs = st_crs(4326)) #update

# ---- Legend remover ----
rm_leg <- function(p) p + theme(legend.position = "none") +
  guides(colour = "none", color = "none", fill = "none")

# ---- Land (QLD) ----
states <- rnaturalearth::ne_states(country = "Australia", returnclass = "sf")
qld <- states[grepl("Queensland", states$name, TRUE) |
              (("name_en" %in% names(states)) & grepl("Queensland", states$name_en, TRUE)), ] |>
  st_crop(bbox)

use_cache  <- TRUE #update
cache_file <- file.path(tempdir(), sprintf("gbr_features_%0.1f-%0.1f_%0.1f-%0.1f.rds", xlim[1], xlim[2], ylim[1], ylim[2])) #update

if (use_cache && file.exists(cache_file)) {
  gbr <- readRDS(cache_file)
} else {
  wfs_base <- "https://maps.eatlas.org.au/maps/wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=ea:GBR_GBRMPA_GBR-features&outputFormat=application/json&srsName=EPSG:4326"
  bbox_q   <- sprintf("&bbox=%f,%f,%f,%f,EPSG:4326", xlim[1], ylim[1], xlim[2], ylim[2])
  gbr_raw <- tryCatch(
    st_read(paste0(wfs_base, bbox_q), quiet = TRUE),
    error = function(e) st_read(wfs_base, quiet = TRUE)
  )
  gbr_fix <- suppressWarnings(
    if ("st_make_valid" %in% ls("package:sf")) sf::st_make_valid(gbr_raw) else st_buffer(gbr_raw, 0)
  )
  gbr <- gbr_fix |> st_crop(bbox) |> st_collection_extract("POLYGON", warn = FALSE)
  if (use_cache) { dir.create(dirname(cache_file), showWarnings = FALSE, recursive = TRUE); saveRDS(gbr, cache_file) }
}

# ---- Palette & transform ----
salinity_cols <- colorRampPalette(c(rgb(48,18,59, maxColorValue = 255), "red","orange","yellow","cyan","blue"))
sal_stretch_trans <- scales::trans_new(
  "sal_stretch",
  transform = function(x) ifelse(x <= 30, x * (0.8/30), 0.8 + (x - 30) * (0.2/6)),
  inverse   = function(y) ifelse(y <= 0.8, y * (30/0.8), 30 + (y - 0.8) * (6/0.2))
)

# ---- Open NetCDF ----
ncfile_alts <- c(
  "https://dapds00.nci.org.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2023-12-18.nc",
  "https://thredds.ereefs.aims.gov.au/thredds/dodsC/fx3/gbr1_2.0/gbr1_simple_2023-12-18.nc"
)

open_nc_safely <- function(urls) {
  for (u in urls) {
    con <- try(open.nc(u), silent = TRUE)
    if (!inherits(con, "try-error")) return(con)
  }
  stop("All OPeNDAP endpoints failed to open.")
}
nc <- open_nc_safely(ncfile_alts); on.exit(close.nc(nc), add = TRUE)

lon <- var.get.nc(nc, "longitude")
lat <- var.get.nc(nc, "latitude")
zc  <- var.get.nc(nc, "zc")

# ---- Safe getter ----
safe_var_get <- function(nc, var, start, count, retries = 6, base_wait = 1.5) {
  for (i in seq_len(retries)) {
    out <- try(var.get.nc(nc, var, start = start, count = count), silent = TRUE)
    if (!inherits(out, "try-error")) return(out)
    Sys.sleep(base_wait * (2^(i - 1)))
  }
  stop("OPeNDAP read failed after retries.")
}

# ---- Map builder ----
make_map <- function(depth_m, time_idx) {
  k_idx <- which.min(abs(zc - (-depth_m)))
  salt  <- safe_var_get(nc, "salt",
                        start = c(1, 1, k_idx, time_idx),
                        count = c(dim(lon)[1], dim(lon)[2], 1, 1))

  df <- data.frame(lon = as.vector(lon),
                   lat = as.vector(lat),
                   salt = as.vector(salt)) |>
        dplyr::filter(!is.na(salt) & salt < 1e30,
                      dplyr::between(lat, ylim[1], ylim[2]),
                      dplyr::between(lon, xlim[1], xlim[2]))

  pts_sf <- st_as_sf(df, coords = c("lon","lat"), crs = 4326)

  ggplot() +
    geom_sf(data = qld, fill = "#f6f2ee", color = NA) +
    geom_sf(data = pts_sf, aes(color = pmin(pmax(salt, 0), 36)), size = 0.2) +
    scale_color_gradientn(
      colours = salinity_cols(256),
      limits  = c(0, 38),
      oob     = scales::squish,
      name    = "Salinity (PSU)",
      breaks  = seq(0, 38, by = 5),
      trans   = sal_stretch_trans
    ) +
    geom_sf(data = gbr, fill = NA, color = "black", size = 0.2, inherit.aes = FALSE) +
    coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
    scale_x_continuous(breaks = seq(145, 147, 1)) +
   scale_y_continuous(breaks = seq(-19, -17, 1)) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text  = element_text(size = 22),
      axis.ticks = element_line(color = "black")
    )
}

# ---- Depths & times ----
depths      <- c(0.5, 2.35, 5.35, 9.00, 13.00)
depth_tags  <- c("0p5","2p35","5p35","9p00","13p00")
time_tags   <- c("0000","1200")
time_map    <- c("0000" = 1, "1200" = 13)

# ---- Generate plots ----
all_plots <- setNames(vector("list", length(time_tags) * length(depth_tags)),
                      as.vector(outer(time_tags, depth_tags, function(tt, dd) paste0("Dec182023_", tt, "_initiationzone_", dd))))

for (ttag in names(time_map)) {
  t_idx <- time_map[[ttag]]
  panels <- setNames(
    mapply(function(d, dtag) make_map(d, t_idx), depths, depth_tags, SIMPLIFY = FALSE),
    paste0("Dec182023_", ttag, "_initiationzone_", depth_tags)
  )
  all_plots[names(panels)] <- panels
}

rows_nl <- lapply(depth_tags, function(dd) lapply(time_tags, function(tt) rm_leg(all_plots[[paste0("Dec182023_", tt, "_initiationzone_", dd)]])))
figure_depth_by_time <- cowplot::plot_grid(plotlist = unlist(rows_nl, recursive = FALSE),
                                           ncol = length(time_tags), align = "hv", axis = "tblr")

# ---- Panel aspect ----
panel_aspect <- diff(ylim) / (diff(xlim) * cos(pi * mean(ylim) / 180))
panel_w <- 4
panel_h <- panel_w * panel_aspect

ncol <- 2
nrow <- length(depth_tags)

# ---- Save ----
out_dir  <- '[insert filepath]' #update
out_file <- file.path(out_dir, "18Dec23_0p5to13m.png")  #update

ggsave(
  filename  = out_file,
  plot      = figure_depth_by_time,
  width     = ncol * panel_w,
  height    = nrow * panel_h,
  dpi       = 300,
  limitsize = FALSE
)

```

# =================================================
# eReefs case studies: time series data extractions
# =================================================

## Timeseries
```{r}
#Notes: GPS Coordinates for each site that timeseries data were extracted are available in Table 1 of the Supplementary material of the manuscript. The code presented provides an example for generating outputs for one site modeled in each region for each event. The same code can be copied and applied to eReefs datasets representing each of these dates and sites to generate the desired timeseries. The example below represents timeseries from 2019 at Green Island, at the southern region of the COTS outbreak initiation zone. 
```

##### Green Isl.
```{r}
# --- INPUTS ---
base_url <- "https://dapds00.nci.org.au/thredds/dodsC/fx3/gbr1_2.0"
dates    <- seq.Date(as.Date("2023-12-13"), as.Date("2023-12-30"), by = "day")

gi_lon   <- 145.966217
gi_lat   <- -16.760406
depths_m <- c(0.5, 2.35, 5.35, 9.0, 13.0)  # requested target depths (m, positive down)
```

###### Base code
```{r}
# --- HELPERS ---
get_nearest_cell <- function(lon, lat, target_lon, target_lat){
  d2 <- (lon - target_lon)^2 + (lat - target_lat)^2
  ij <- arrayInd(which.min(d2), dim(d2))
  c(ii = ij[1], jj = ij[2])
}

nearest_k_for_depth <- function(zc, d_m) which.min(abs(zc - (-d_m)))

# --- MAIN DAY EXTRACTOR (skips depths deeper than seabed) ---
extract_day <- function(date){
  url <- sprintf("%s/gbr1_simple_%s.nc", base_url, format(date, "%Y-%m-%d"))
  message("Opening: ", url)

  nc <- try(open.nc(url), silent = TRUE)
  if (inherits(nc, "try-error")) return(NULL)
  on.exit(close.nc(nc), add = TRUE)

  lon <- var.get.nc(nc, "longitude")
  lat <- var.get.nc(nc, "latitude")
  zc  <- var.get.nc(nc, "zc")              # positive up (depths are negative)
  tim <- var.get.nc(nc, "time")            # hours since origin
  t_units <- att.get.nc(nc, "time", "units")
  origin  <- ymd_hms(sub("hours since ", "", t_units), tz = "UTC")
  times   <- origin + dhours(tim)

  cell <- get_nearest_cell(lon, lat, gi_lon, gi_lat)

  # Determine which vertical levels at this (i,j) contain finite data for the day
  valid_k <- which(sapply(seq_along(zc), function(k){
    s <- var.get.nc(nc, "salt",
                    start = c(cell["ii"], cell["jj"], k, 1),
                    count = c(1, 1, 1, length(times)))
    any(is.finite(s) & s < 1e30)
  }))
  if (length(valid_k) == 0) return(NULL)

  seabed_depth_m <- max(-zc[valid_k])  # deepest valid depth (m, positive down)
  depths_today   <- depths_m[depths_m <= seabed_depth_m + 1e-6]

  dropped <- setdiff(depths_m, depths_today)
  if (length(dropped) > 0) {
    message(sprintf("Date %s: dropping depths deeper than seabed (%.1f m): %s",
                    format(date, "%Y-%m-%d"), seabed_depth_m, paste(dropped, collapse = ", ")))
  }

  # Extract for valid depths only
  out <- lapply(depths_today, function(d){
    kk <- nearest_k_for_depth(zc, d)
    salt <- var.get.nc(nc, "salt",
                       start = c(cell["ii"], cell["jj"], kk, 1),
                       count = c(1, 1, 1, length(times)))
    tibble(datetime = times,
           depth_m  = d,
           salinity = as.numeric(salt),
           date     = date)
  })

  bind_rows(out)
}

# --- RUN ACROSS ALL DAYS ---
df_all <- map_dfr(dates, extract_day)
```

###### Green Isl. plot (example)
```{r}
# --- PLOT (daily x-ticks, 28â36 PSU, no grid, Times New Roman) ---
GreenIsl_Dec2023<-ggplot(df_all, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"),
                 by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Dec 13th - 30th 2023)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    panel.grid  = element_blank()
  )

GreenIsl_Dec2023+
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1))
```

###### Consistent depth colours 
```{r}
# --- Directory for output ---
mid_dir <- '[insert filepath]'
dir.create(out_dir, recursive = TRUE, showWarnings = FALSE)

# --- Plot list (titles -> plot objects) ---
midshelfplots <- list(
  "Green Island"        = GreenIsl_Dec2023
  #add the rest of the sites
)

# --- Save loop ---
for (ttl in names(midshelfplots)) {
  file_safe <- gsub("[^A-Za-z0-9]+", "_", ttl)  # safe filename
  ggsave(
    filename = file.path(mid_dir, paste0(file_safe, "_Dec2023.png")),
    plot = midshelfplots[[ttl]] +
      scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
      #scale_x_datetime(date_breaks = "1 day", date_labels = "%d %b") +
      scale_colour_depth() +
      ggtitle(ttl) +
      theme(
        axis.text.x  = element_text(angle = 45, hjust = 1),
        axis.title.x = element_blank()
      ),
    width = 8, height = 4, dpi = 300
  )
}
```


```{r}
# These code use eReefs data that regared modelled reef locations in Clements & Byrne 2026. This code was used to produce the "minimum salinity" and "Days below 30 PSU" for each location during a designated time frame for each plume event. 

# The data relating to this code are available in XXXX and are presented in Supplementary Table 8 and 9 in the manuscript. 
```

# =============================================================
# Time series data summaries (salinity minima and days <30 PSU)
# =============================================================

# Important note
```{r}
# Data for the below code was extracted from the eReefs data extraction tool: https://extraction.ereefs.aims.gov.au/extract-data but is also available on request from Matthew.clements@sydney.edu.au
```


# Packages
```{r}
library(RNetCDF)
library(lubridate)
library(dplyr)
library(purrr)
library(tibble)
library(ggplot2)
library(readr)
library(tidyr)
library(stringr)
```

# 2010/11
### Normanby
#### Hedge Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/PCB/2509.d658e7f-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Hedge Reef)
target_lat <- -13.974437
target_lon <- 143.882324

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
}
parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord) { d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x * pi/180
haversine_km <- function(lat1, lon1, lat2, lon2){
  R <- 6371
  dlat <- deg2rad(lat2 - lat1); dlon <- deg2rad(lon2 - lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)

time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))

if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT HEDGE REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)) {
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups = "drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else {
  sel_site <- "Hedge Reef"
}

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>%
  filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>%
  summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")
if (nrow(ds) == 0) stop("No Hedge Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_hr <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_hr) > 0)

# ---- PLOT (same aesthetics) ----
HedgeReef_FebMar2011 <- ggplot(df_hr, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(0, 36), breaks = seq(0, 36, by = 2)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

HedgeReef_FebMar2011
# ggsave("HedgeReef_FebMar2011.png", HedgeReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

#### Quoine Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/PCB/2509.d658e7f-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Quion Reef)
target_lat <- -12.432203
target_lon <- 143.509225

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
}
parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd",
              "dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy",
              "Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord) { d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x * pi/180
haversine_km <- function(lat1, lon1, lat2, lon2){
  R <- 6371
  dlat <- deg2rad(lat2 - lat1); dlon <- deg2rad(lon2 - lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)

time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))

if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT QUION REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)) {
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups = "drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else {
  sel_site <- "Quion Reef"
}

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>%
  filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>%
  summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")
if (nrow(ds) == 0) stop("No Quion Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_qr <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_qr) > 0)

# ---- PLOT (same aesthetics) ----
QuionReef_FebMar2011 <- ggplot(df_qr, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(0, 36), breaks = seq(0, 36, by = 2)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

QuionReef_FebMar2011
# ggsave("QuionReef_FebMar2011.png", QuionReef_FebMar2011, width = 12, height = 6, dpi = 300)

```
#### Burke Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/PCB/2509.d658e7f-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Burke Reef)
target_lat <- -12.644159
target_lon <- 143.572446

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT BURKE REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Burke Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Burke Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_brk <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_brk) > 0)

# ---- PLOT (same aesthetics) ----
BurkeReef_FebMar2011 <- ggplot(df_brk, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(0, 36), breaks = seq(0, 36, by = 2)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BurkeReef_FebMar2011
# ggsave("BurkeReef_FebMar2011.png", BurkeReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

###Initiation zone
#### Green Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder/2509.6598bcf-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nm <- nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
  nm
}

parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd",
              "dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy",
              "Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}

map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail))
  out  <- rep(NA_real_, length(req))
  ord  <- order(req) # shallow -> deep
  for (i in ord) {
    d <- abs(avail - req[i]); d[used] <- Inf
    j <- which.min(d)
    if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE }
  }
  out
}

# ---- LOAD ----
raw <- read_csv(csv_path, show_col_types = FALSE)
nms <- names(raw)

# Likely columns (flexible detection)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$", "^Date\\s*Time$", "^Date.?Time$", "^timestamp$", "^datetime$", "^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))  # optional separate time column
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))

if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$"))
  raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]])
  time_col <- ".__combo_dt"
}

stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

# Standardize fields
dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_
  )

# Filter site â "Green Island", window, and salt variable if present
site_ok <- grepl("\\bgreen\\s*is(l(?:\\.|and)?)?\\b", dat$site, ignore.case = TRUE)
dat <- dat %>% filter(site_ok, datetime >= t_start, datetime <= t_end)
if (!all(is.na(dat$variable))) dat <- dat %>% filter(variable %in% c("salt","salinity","sal"))

# Aggregate duplicates
gi_salt <- dat %>%
  filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>%
  summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")

if (nrow(gi_salt) == 0) stop("No Green Island salinity rows in the requested window after parsing. Check column names/values.")

# One-to-one nearest depth mapping
avail <- sort(unique(gi_salt$depth_m))
if (length(avail) == 0) stop("No depth values available after filtering.")
mapped_avail <- map_unique(avail, depths_req)
names(mapped_avail) <- depths_req
mapped_avail <- mapped_avail[!is.na(mapped_avail)]

tol <- 1e-6
df_all <- bind_rows(lapply(names(mapped_avail), function(d_req){
  d_av <- mapped_avail[[d_req]]
  gi_salt %>%
    filter(abs(depth_m - d_av) < tol) %>%
    mutate(depth_m = as.numeric(d_req))
}))

stopifnot(nrow(df_all) > 0)

# ---- PLOT (same aesthetics, extended to Mar 28) ----
GreenIsl_Feb2011 <- ggplot(df_all, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

GreenIsl_Feb2011
# ggsave("GreenIsl_Feb2011.png", GreenIsl_Feb2011, width = 12, height = 6, dpi = 300)

```

#### Cairns Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder/2509.6598bcf-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
}
parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd",
              "dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy",
              "Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord) { d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE } }
  out
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))
dat <- raw %>%
  mutate(datetime = parse_dt_flex(.data[[time_col]]),
         site     = as.character(.data[[site_col]]),
         depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
         value    = suppressWarnings(as.numeric(.data[[val_col]])),
         variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_)

# ---- SITE FUNCTION (reuses aesthetics) ----
plot_sal_site <- function(site_regex){
  d <- dat %>% filter(grepl(site_regex, site, ignore.case = TRUE),
                      datetime >= t_start, datetime <= t_end)
  if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))
  ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
        group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")
  if (nrow(ds) == 0) stop("No rows for site pattern: ", site_regex)
  avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
  mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]
  tol <- 1e-6
  df  <- bind_rows(lapply(names(mapped), function(d_req){
          d_av <- mapped[[d_req]]
          ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
        }))
  ggplot(df, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
    geom_line(alpha = 0.9) +
    scale_x_datetime(
      breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                   as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
      date_labels = "%d-%b"
    ) +
    scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
    theme_bw(base_size = 13, base_family = "Times New Roman") +
    labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())
}

# ---- BUILD PLOTS ----
GreenIsl_FebMar2011   <- plot_sal_site("\\bgreen\\s*is(l(?:\\.|and)?)?\\b")
CairnsReef_FebMar2011 <- plot_sal_site("\\bcairns\\s*reef\\b")

# Print Cairns Reef
CairnsReef_FebMar2011

# Optional saves:
# ggsave("GreenIsl_FebMar2011.png", GreenIsl_FebMar2011, width = 12, height = 6, dpi = 300)
# ggsave("CairnsReef_FebMar2011.png", CairnsReef_FebMar2011, width = 12, height = 6, dpi = 300)

```
#### Lizard Island 
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder/2509.6598bcf-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
}
parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd",
              "dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy",
              "Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord) { d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE } }
  out
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_
  )

# ---- Lizard Island plot ----
# Accepts "Lizard Island", "Lizard Isl.", etc.
site_regex <- "\\blizard\\s*is(l(?:\\.|and)?)?\\b"

d <- dat %>% filter(grepl(site_regex, site, ignore.case = TRUE),
                    datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

ds <- d %>% 
  filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% 
  summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")

if (nrow(ds) == 0) stop("No Lizard Island salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_li <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))

stopifnot(nrow(df_li) > 0)

LizardIsl_FebMar2011 <- ggplot(df_li, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

LizardIsl_FebMar2011
# ggsave("LizardIsl_FebMar2011.png", LizardIsl_FebMar2011, width = 12, height = 6, dpi = 300)

```
#### Batt Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder 2/2509.a43080d-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Batt Reef)
target_lat <- -16.376361
target_lon <- 145.690887

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
}
parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord) { d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x * pi/180
haversine_km <- function(lat1, lon1, lat2, lon2){
  R <- 6371
  dlat <- deg2rad(lat2 - lat1); dlon <- deg2rad(lon2 - lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)

time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))

if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT BATT REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)) {
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups = "drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else {
  sel_site <- "Batt Reef"
}

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>%
  filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>%
  summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")
if (nrow(ds) == 0) stop("No Batt Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_br <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_br) > 0)

# ---- PLOT (same aesthetics) ----
BattReef_FebMar2011 <- ggplot(df_br, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BattReef_FebMar2011
# ggsave("BattReef_FebMar2011.png", BattReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

#### Tongue Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder 2/2509.a43080d-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Tongue Reef)
target_lat <- -16.258228
target_lon <- 145.643181

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
}
parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord) { d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x * pi/180
haversine_km <- function(lat1, lon1, lat2, lon2){
  R <- 6371
  dlat <- deg2rad(lat2 - lat1); dlon <- deg2rad(lon2 - lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)

time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))

if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT TONGUE REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)) {
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups = "drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else {
  sel_site <- "Tongue Reef"
}

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>%
  filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>%
  summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")
if (nrow(ds) == 0) stop("No Tongue Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_tr <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_tr) > 0)

# ---- PLOT (same aesthetics) ----
TongueReef_FebMar2011 <- ggplot(df_tr, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

TongueReef_FebMar2011
# ggsave("TongueReef_FebMar2011.png", TongueReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

#### Undine Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder 2/2509.a43080d-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Undine Reef Cay)
target_lat <- -16.109505
target_lon <- 145.633678

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
}
parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord) { d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x * pi/180
haversine_km <- function(lat1, lon1, lat2, lon2){
  R <- 6371
  dlat <- deg2rad(lat2 - lat1); dlon <- deg2rad(lon2 - lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)

time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))

if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT UNDINE REEF CAY BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)) {
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups = "drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else {
  sel_site <- "Undine Reef Cay"
}

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>%
  filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>%
  summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")
if (nrow(ds) == 0) stop("No Undine Reef Cay salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_urc <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_urc) > 0)

# ---- PLOT (same aesthetics) ----
UndineReefCay_FebMar2011 <- ggplot(df_urc, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

UndineReefCay_FebMar2011
# ggsave("UndineReefCay_FebMar2011.png", UndineReefCay_FebMar2011, width = 12, height = 6, dpi = 300)

```

#### Williamson Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder 2/2509.a43080d-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Williamson Reef)
target_lat <- -15.36905
target_lon <- 145.5472

# ---- HELPERS ----
pick_col <- function(nms, patterns) {
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case = TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case = TRUE))][1]
}
parse_dt_flex <- function(x) {
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders = orders, tz = "UTC", truncated = 3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord) { d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])) { out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x * pi/180
haversine_km <- function(lat1, lon1, lat2, lon2){
  R <- 6371
  dlat <- deg2rad(lat2 - lat1); dlon <- deg2rad(lon2 - lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)

time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))

if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)) {
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT WILLIAMSON REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)) {
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups = "drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else {
  sel_site <- "Williamson Reef"
}

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>%
  filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>%
  summarise(salinity = mean(value, na.rm = TRUE), .groups = "drop")
if (nrow(ds) == 0) stop("No Williamson Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_wr <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_wr) > 0)

# ---- PLOT (same aesthetics) ----
WilliamsonReef_FebMar2011 <- ggplot(df_wr, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

WilliamsonReef_FebMar2011
# ggsave("WilliamsonReef_FebMar2011.png", WilliamsonReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

##### Data summaries IZ_1
```{r}
library(readr)
library(dplyr)
library(lubridate)
library(tidyr)

# 1) Load and clean
path <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder/2509.6598bcf-collected.csv'
df <- read_csv(path, show_col_types = FALSE)

# 2) Robust datetime + numeric coercions
tmp <- gsub("T", " ", df$`Aggregated Date/Time`)
dt  <- as.POSIXct(tmp, format = "%Y-%m-%d %H:%M:%S", tz = "UTC")
na  <- is.na(dt)
dt[na] <- as.POSIXct(tmp[na], format = "%Y-%m-%d %H:%M", tz = "UTC")

df <- df %>%
  mutate(
    `Aggregated Date/Time` = dt,
    date_only = as_date(`Aggregated Date/Time`),
    lowest = readr::parse_number(as.character(lowest)),
    Depth  = readr::parse_number(as.character(Depth))
  ) %>%
  filter(!is.na(`Aggregated Date/Time`))

# 3) Restrict to 13â30 Dec of the dataset year
yr <- year(min(df$`Aggregated Date/Time`, na.rm = TRUE))
win <- df %>%
  filter(
    `Aggregated Date/Time` >= ymd_hms(sprintf("%d-02-15 00:00:00", yr), tz = "UTC"),
    `Aggregated Date/Time` <= ymd_hms(sprintf("%d-03-28 23:59:59", yr), tz = "UTC")
  )

# 4) Minimum salinity (value + timestamp) per site/depth
min_sal <- win %>%
  filter(!is.na(lowest)) %>%
  arrange(`Site Name`, Depth, lowest, `Aggregated Date/Time`) %>%
  group_by(`Site Name`, Depth) %>%
  slice(1) %>%
  ungroup() %>%
  transmute(`Site Name`, Depth, `Min Salinity` = lowest, Date = `Aggregated Date/Time`)

# 5) Unique calendar days with salinity < 30 PSU per site/depth
below_30 <- win %>%
  filter(!is.na(lowest), lowest < 30) %>%
  group_by(`Site Name`, Depth) %>%
  summarise(
    Days_below_30 = n_distinct(date_only),
    Dates = list(sort(unique(date_only))),
    .groups = "drop"
  )

# 6) Joined summary
summary_tbl <- min_sal %>%
  left_join(below_30, by = c("Site Name", "Depth")) %>%
  arrange(`Site Name`, Depth)

# 7) Inspect
print(head(min_sal, 10))
print(head(below_30, 10))
print(head(summary_tbl, 10))
```

##### Data summaries IZ_2
```{r}
library(readr)
library(dplyr)
library(lubridate)
library(tidyr)

# 1) Load and clean
path <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Initiation zone/untitled folder 2/2509.a43080d-collected.csv'
df <- read_csv(path, show_col_types = FALSE)

# 2) Robust datetime + numeric coercions
tmp <- gsub("T", " ", df$`Aggregated Date/Time`)
dt  <- as.POSIXct(tmp, format = "%Y-%m-%d %H:%M:%S", tz = "UTC")
na  <- is.na(dt)
dt[na] <- as.POSIXct(tmp[na], format = "%Y-%m-%d %H:%M", tz = "UTC")

df <- df %>%
  mutate(
    `Aggregated Date/Time` = dt,
    date_only = as_date(`Aggregated Date/Time`),
    lowest = readr::parse_number(as.character(lowest)),
    Depth  = readr::parse_number(as.character(Depth))
  ) %>%
  filter(!is.na(`Aggregated Date/Time`))

# 3) Restrict to 13â30 Dec of the dataset year
yr <- year(min(df$`Aggregated Date/Time`, na.rm = TRUE))
win <- df %>%
  filter(
    `Aggregated Date/Time` >= ymd_hms(sprintf("%d-02-15 00:00:00", yr), tz = "UTC"),
    `Aggregated Date/Time` <= ymd_hms(sprintf("%d-03-28 23:59:59", yr), tz = "UTC")
  )

# 4) Minimum salinity (value + timestamp) per site/depth
min_sal <- win %>%
  filter(!is.na(lowest)) %>%
  arrange(`Site Name`, Depth, lowest, `Aggregated Date/Time`) %>%
  group_by(`Site Name`, Depth) %>%
  slice(1) %>%
  ungroup() %>%
  transmute(`Site Name`, Depth, `Min Salinity` = lowest, Date = `Aggregated Date/Time`)

# 5) Unique calendar days with salinity < 30 PSU per site/depth
below_30 <- win %>%
  filter(!is.na(lowest), lowest < 30) %>%
  group_by(`Site Name`, Depth) %>%
  summarise(
    Days_below_30 = n_distinct(date_only),
    Dates = list(sort(unique(date_only))),
    .groups = "drop"
  )

# 6) Joined summary
summary_tbl <- min_sal %>%
  left_join(below_30, by = c("Site Name", "Depth")) %>%
  arrange(`Site Name`, Depth)

# 7) Inspect
print(head(min_sal, 10))
print(head(below_30, 10))
print(head(summary_tbl, 10))
```

### Russel-Mulgrave-Townsville
#### Sudbury Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Russel Mulgrave-Townsville/2509.2896a76-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Sudbury Reef)
target_lat <- -17.024989
target_lon <- 146.163987

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT SUDBURY REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Sudbury Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Sudbury Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_sr <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_sr) > 0)

# ---- PLOT (same aesthetics) ----
SudburyReef_FebMar2011 <- ggplot(df_sr, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

SudburyReef_FebMar2011
# ggsave("SudburyReef_FebMar2011.png", SudburyReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

#### Flora Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Russel Mulgrave-Townsville/2509.2896a76-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Flora Reef)
target_lat <- -17.196385
target_lon <- 146.280083

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT FLORA REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Flora Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Flora Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_fr <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_fr) > 0)

# ---- PLOT (same aesthetics) ----
FloraReef_FebMar2011 <- ggplot(df_fr, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

FloraReef_FebMar2011
# ggsave("FloraReef_FebMar2011.png", FloraReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

#### Ellison Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Russel Mulgrave-Townsville/2509.2896a76-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Ellison Reef)
target_lat <- -17.727626
target_lon <- 146.373954

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT ELLISON REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Ellison Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Ellison Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_er <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_er) > 0)

# ---- PLOT (same aesthetics) ----
EllisonReef_FebMar2011 <- ggplot(df_er, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

EllisonReef_FebMar2011
# ggsave("EllisonReef_FebMar2011.png", EllisonReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

#### Otter Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/2010:11/Russel Mulgrave-Townsville/2509.2896a76-collected.csv'
t_start   <- ymd("2011-02-15", tz = "UTC")
t_end     <- ymd("2011-03-28", tz = "UTC")
depths_req <- c(0.5, 3.00, 5.55, 8.80, 12.75)

# Target site (Otter Reef)
target_lat <- -18.104842
target_lon <- 146.500117

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
OtterReef_FebMar2011 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2011-02-15 00:00:00", tz = "UTC"),
                 as.POSIXct("2011-03-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

OtterReef_FebMar2011
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)

```

#2018/19
### Initiation Zone 
#### Nearshore
##### Gubbins Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -15.70672
target_lon <- 145.3946

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
GubbinsReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

GubbinsReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Struck Isl
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.172621
target_lon <- 145.445266

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
StruckIsl_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

StruckIsl_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Snapper Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.293608
target_lon <- 145.491028

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
SnapperIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(0, 36), breaks = seq(0, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

SnapperIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Double Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.733975
target_lon <- 145.678734

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
DoubleIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(15, 36), breaks = seq(15, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

DoubleIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Inner-Shelf
##### Hope Island Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -15.73473
target_lon <- 145.4497

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
HopeIslandReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

HopeIslandReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Low Isles
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.387148
target_lon <- 145.555313

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
LowIsles_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

LowIsles_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Mid-Shelf
##### Cairns Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -15.72556
target_lon <- 145.5361

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
CairnsReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

CairnsReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Undine Reef Cay
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.109505
target_lon <- 145.633678

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
UndineReefCay_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

UndineReefCay_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Tongue Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.258228
target_lon <- 145.643181

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
TongueReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

TongueReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Batt Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.376361
target_lon <- 145.690887

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
BattReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BattReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Green Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.760406
target_lon <- 145.966217

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
GreenIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

GreenIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Lizard Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -14.68009
target_lon <- 145.4425

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
LizardIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

LizardIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Williamson Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -15.36905
target_lon <- 145.5472

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
WilliamsonReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

WilliamsonReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

### Russel-Mulgrave/Townsville 
#### Nearshore
##### Barnard Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.672147
target_lon <- 146.166195

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
BarnardIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BarnardIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Dunk Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.933084
target_lon <- 146.13643

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
DunkIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

DunkIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Maggie Island 
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -19.129344
target_lon <- 146.774551

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
MaggieIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

MaggieIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Inner-shelf
##### Orpheus Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.569305
target_lon <- 146.478116

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
OrpheusIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

OrpheusIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Frankland Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.209443
target_lon <- 146.070974

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
FranklandIsland_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

FranklandIsland_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Mid-shelf
##### Bramble Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.384438
target_lon <- 146.656491

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
BrambleReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BrambleReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Rib Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.488131
target_lon <- 146.864105

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
RibReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

RibReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Flora Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.196385
target_lon <- 146.280083

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
FloraReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

FloraReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Ellison Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.727626
target_lon <- 146.373954

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
EllisonReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

EllisonReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Otter Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.104842
target_lon <- 146.500117

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
OtterReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

OtterReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Sudbury Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.024989
target_lon <- 146.163987

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
SudburyReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

SudburyReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Keeper Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-01-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.75726
target_lon <- 147.2573

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
KeeperReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

KeeperReef_2019
```

### Burdekin
#### Innershelf
##### Holbourne Reef 
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-02-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -19.72884
target_lon <- 148.3518

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
HolbourneReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

HolbourneReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Morinda shoal 
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-02-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -19.14826
target_lon <- 147.6383

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
Morindashoal_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

Morindashoal_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Tink Shoal
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-02-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -19.72884
target_lon <- 148.3518

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
TinkShoal_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

TinkShoal_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Midshelf
##### Old Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-02-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -19.40119
target_lon <- 148.0742

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
OldReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

OldReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)

library(sf)
citation('sf')
```

##### Big Broadhurst Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-02-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.9493
target_lon <- 147.7259

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
BigBroadhurstReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BigBroadhurstReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Dingo Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-02-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -19.19277
target_lon <- 148.3385

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
DingoReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

DingoReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Kangaroo Reef
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
t_start   <- ymd("2019-02-01", tz = "UTC")
t_end     <- ymd("2019-02-28", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -19.28372
target_lon <- 148.546

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
KangarooReef_2019 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2019-01-01 00:00:00", tz = "UTC"),
                 as.POSIXct("2019-02-28 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

KangarooReef_2019
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

###### Data summaries All regions 2018/19
```{r}
library(readr)
library(dplyr)
library(lubridate)
library(tidyr)

# 1) Load and clean
path <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Jan-March 2019/2509.64eb699-collected.csv'
df <- read_csv(path, show_col_types = FALSE)

# 2) Robust datetime + numeric coercions
tmp <- gsub("T", " ", df$`Aggregated Date/Time`)
dt  <- as.POSIXct(tmp, format = "%Y-%m-%d %H:%M:%S", tz = "UTC")
na  <- is.na(dt)
dt[na] <- as.POSIXct(tmp[na], format = "%Y-%m-%d %H:%M", tz = "UTC")

df <- df %>%
  mutate(
    `Aggregated Date/Time` = dt,
    date_only = as_date(`Aggregated Date/Time`),
    lowest = readr::parse_number(as.character(lowest)),
    Depth  = readr::parse_number(as.character(Depth))
  ) %>%
  filter(!is.na(`Aggregated Date/Time`))

# 3) Restrict to 13â30 Dec of the dataset year
yr <- year(min(df$`Aggregated Date/Time`, na.rm = TRUE))
win <- df %>%
  filter(
    `Aggregated Date/Time` >= ymd_hms(sprintf("%d-01-01 00:00:00", yr), tz = "UTC"),
    `Aggregated Date/Time` <= ymd_hms(sprintf("%d-02-28 23:59:59", yr), tz = "UTC")
  )

# 4) Minimum salinity (value + timestamp) per site/depth
min_sal <- win %>%
  filter(!is.na(lowest)) %>%
  arrange(`Site Name`, Depth, lowest, `Aggregated Date/Time`) %>%
  group_by(`Site Name`, Depth) %>%
  slice(1) %>%
  ungroup() %>%
  transmute(`Site Name`, Depth, `Min Salinity` = lowest, Date = `Aggregated Date/Time`)

# 5) Unique calendar days with salinity < 30 PSU per site/depth
below_30 <- win %>%
  filter(!is.na(lowest), lowest < 30) %>%
  group_by(`Site Name`, Depth) %>%
  summarise(
    Days_below_30 = n_distinct(date_only),
    Dates = list(sort(unique(date_only))),
    .groups = "drop"
  )

# 6) Joined summary
summary_tbl <- min_sal %>%
  left_join(below_30, by = c("Site Name", "Depth")) %>%
  arrange(`Site Name`, Depth)

# 7) Inspect
print(head(min_sal, 10))
print(head(below_30, 10))
print(head(summary_tbl, 10))
```

#Dec 2023
### Initiation Zone 
#### Nearshore
##### Gubbins Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -15.70672
target_lon <- 145.3946

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
GubbinsReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = ", UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

GubbinsReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Struck Isl
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.172621
target_lon <- 145.445266

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
StruckIsl_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(10, 36), breaks = seq(10, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = ", UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

StruckIsl_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Snapper Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.293608
target_lon <- 145.491028

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
SnapperIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(10, 36), breaks = seq(10, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

SnapperIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Double Island
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.733975
target_lon <- 145.678734

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
DoubleIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(0, 36), breaks = seq(0, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

DoubleIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Innershelf
##### Hope Island Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -15.73473
target_lon <- 145.4497

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
HopeIslandReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

HopeIslandReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Low Isles
```{r}
# ---- PACKAGES ----
library(readr); library(lubridate); library(dplyr); library(stringr); library(purrr); library(tibble); library(ggplot2)

# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.387148
target_lon <- 145.555313

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
LowIsles_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

LowIsles_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Mid Shelf
##### Cairns Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -15.72556
target_lon <- 145.5361

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
CairnsReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

CairnsReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Undine Reef Cay
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.109505
target_lon <- 145.633678

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
UndineReefCay_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

UndineReefCay_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Tongue Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.258228
target_lon <- 145.643181

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
TongueReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

TongueReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Batt Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.376361
target_lon <- 145.690887

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
BattReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BattReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Green Island
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -16.760406
target_lon <- 145.966217

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
GreenIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

GreenIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Lizard Island
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -14.68009
target_lon <- 145.4425

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
LizardIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

LizardIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Williamson Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -15.36905
target_lon <- 145.5472

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
WilliamsonReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

WilliamsonReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

###### Data summaries IZ_Dec 2023
```{r}
# 1) Load and clean
path <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/Figures/Mulgrave - Twnsville/Time series/Data/2509.869f63b-collected.csv'
df <- read_csv(path, show_col_types = FALSE)

# 2) Robust datetime + numeric coercions
tmp <- gsub("T", " ", df$`Aggregated Date/Time`)
dt  <- as.POSIXct(tmp, format = "%Y-%m-%d %H:%M:%S", tz = "UTC")
na  <- is.na(dt)
dt[na] <- as.POSIXct(tmp[na], format = "%Y-%m-%d %H:%M", tz = "UTC")

df <- df %>%
  mutate(
    `Aggregated Date/Time` = dt,
    date_only = as_date(`Aggregated Date/Time`),
    lowest = readr::parse_number(as.character(lowest)),
    Depth  = readr::parse_number(as.character(Depth))
  ) %>%
  filter(!is.na(`Aggregated Date/Time`))

# 3) Restrict to 13â30 Dec of the dataset year
yr <- year(min(df$`Aggregated Date/Time`, na.rm = TRUE))
win <- df %>%
  filter(
    `Aggregated Date/Time` >= ymd_hms(sprintf("%d-12-13 00:00:00", yr), tz = "UTC"),
    `Aggregated Date/Time` <= ymd_hms(sprintf("%d-12-30 23:59:59", yr), tz = "UTC")
  )

# 4) Minimum salinity (value + timestamp) per site/depth
min_sal <- win %>%
  filter(!is.na(lowest)) %>%
  arrange(`Site Name`, Depth, lowest, `Aggregated Date/Time`) %>%
  group_by(`Site Name`, Depth) %>%
  slice(1) %>%
  ungroup() %>%
  transmute(`Site Name`, Depth, `Min Salinity` = lowest, Date = `Aggregated Date/Time`)

# 5) Unique calendar days with salinity < 30 PSU per site/depth
below_30 <- win %>%
  filter(!is.na(lowest), lowest < 30) %>%
  group_by(`Site Name`, Depth) %>%
  summarise(
    Days_below_30 = n_distinct(date_only),
    Dates = list(sort(unique(date_only))),
    .groups = "drop"
  )

# 6) Joined summary
summary_tbl <- min_sal %>%
  left_join(below_30, by = c("Site Name", "Depth")) %>%
  arrange(`Site Name`, Depth)

# 7) Inspect
print(head(min_sal, 10))
print(head(below_30, 10))
print(head(summary_tbl, 10))
```

### Russel-Mulgrave/Townsville 
#### Nearshore
##### Barnard Island
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.672147
target_lon <- 146.166195

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
BarnardIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(10, 36), breaks = seq(10, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BarnardIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Dunk Island
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.933084
target_lon <- 146.13643

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
DunkIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

DunkIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Maggie Island 
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 


target_lat <- -19.129344
target_lon <- 146.774551

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Maggie Island"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
MaggieIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 37), breaks = seq(20, 37, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

MaggieIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Fitzroy Island 
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 


target_lat <- -16.936596
target_lon <- 145.978566

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Fitzroy Island "

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Fitzroy Island  salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
Fitzroy_Island_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 37), breaks = seq(20, 37, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

Fitzroy_Island_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Innershelf
##### Frankland Island
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.209443
target_lon <- 146.070974

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
FranklandIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

FranklandIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Orpheus Island
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.569305
target_lon <- 146.478116

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
OrpheusIsland_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

OrpheusIsland_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

#### Midshelf
##### Bramble Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.384438
target_lon <- 146.656491

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
BrambleReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

BrambleReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```


##### Rib Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.488131
target_lon <- 146.864105

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
RibReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

RibReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Flora Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.196385
target_lon <- 146.280083

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
FloraReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

FloraReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Ellison Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.727626
target_lon <- 146.373954

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
EllisonReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

EllisonReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Otter Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.869f63b-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.104842
target_lon <- 146.500117

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Otter Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Otter Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
OtterReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

OtterReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Keeper Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.e875848-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -18.75726
target_lon <- 147.2573

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Keeper Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Keeper Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
KeeperReef_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

KeeperReef_2023
# ggsave("OtterReef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

##### Sudbury Reef
```{r}
# ---- INPUTS ----
csv_path  <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.e875848-collected.csv'
t_start   <- ymd("2023-12-13", tz = "UTC")
t_end     <- ymd("2023-12-30", tz = "UTC")
depths_req <- c(0.5, 2.35, 5.35, 9.0, 13.0) 

# Target site (Otter Reef)
target_lat <- -17.024989
target_lon <- 146.163987

# ---- HELPERS ----
pick_col <- function(nms, patterns){
  ix <- which(vapply(patterns, function(p) any(str_detect(nms, regex(p, ignore_case=TRUE))), logical(1)))[1]
  if (is.na(ix)) return(NA_character_)
  nms[str_detect(nms, regex(patterns[ix], ignore_case=TRUE))][1]
}
parse_dt_flex <- function(x){
  x <- trimws(as.character(x))
  orders <- c("ymd HMS","ymd HM","ymd H","ymd","dmy HMS","dmy HM","dmy H","dmy",
              "mdy HMS","mdy HM","mdy H","mdy","Ymd HMS","Ymd HM","Ymd H","Ymd")
  suppressWarnings(parse_date_time(x, orders=orders, tz="UTC", truncated=3))
}
map_unique <- function(avail, req){
  used <- rep(FALSE, length(avail)); out <- rep(NA_real_, length(req)); ord <- order(req)
  for (i in ord){ d <- abs(avail - req[i]); d[used] <- Inf; j <- which.min(d); if (is.finite(d[j])){ out[i] <- avail[j]; used[j] <- TRUE } }
  out
}
deg2rad <- function(x) x*pi/180
haversine_km <- function(lat1,lon1,lat2,lon2){
  R <- 6371; dlat <- deg2rad(lat2-lat1); dlon <- deg2rad(lon2-lon1)
  a <- sin(dlat/2)^2 + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(dlon/2)^2
  2*R*atan2(sqrt(a), sqrt(1-a))
}

# ---- LOAD + STANDARDIZE ----
raw <- read_csv(csv_path, show_col_types = FALSE); nms <- names(raw)
time_col <- pick_col(nms, c("^Aggregated\\s*Date/?Time$","^Date\\s*Time$","^Date.?Time$","^timestamp$","^datetime$","^date$"))
time2_col <- pick_col(nms, c("^time$","^hour$"))
site_col <- pick_col(nms, c("^Site\\s*Name$","^site$","^station$"))
var_col  <- pick_col(nms, c("^Variable$","^var$"))
depth_col<- pick_col(nms, c("^Depth(\\s*\\(m\\))?$","^depth$","^z$"))
val_col  <- pick_col(nms, c("^mean$","^value$","^measurement$","^val$","^sal(inity)?$","^salt$"))
lat_col  <- pick_col(nms, c("^lat(itude)?$","^y$"))
lon_col  <- pick_col(nms, c("^lon(gitude)?$","^x$"))
if (is.na(time_col) && !is.na(pick_col(nms, c("^date$"))) && !is.na(time2_col)){
  date_col <- pick_col(nms, c("^date$")); raw$.__combo_dt <- paste(raw[[date_col]], raw[[time2_col]]); time_col <- ".__combo_dt"
}
stopifnot(!is.na(time_col), !is.na(site_col), !is.na(depth_col), !is.na(val_col))

dat <- raw %>%
  mutate(
    datetime = parse_dt_flex(.data[[time_col]]),
    site     = as.character(.data[[site_col]]),
    depth_m  = suppressWarnings(as.numeric(.data[[depth_col]])),
    value    = suppressWarnings(as.numeric(.data[[val_col]])),
    variable = if (!is.na(var_col)) tolower(as.character(.data[[var_col]])) else NA_character_,
    lat      = if (!is.na(lat_col)) suppressWarnings(as.numeric(.data[[lat_col]])) else NA_real_,
    lon      = if (!is.na(lon_col)) suppressWarnings(as.numeric(.data[[lon_col]])) else NA_real_
  )

# ---- SELECT OTTER REEF BY COORDS (fallback to name) ----
if (!is.na(lat_col) && !is.na(lon_col)){
  site_centers <- dat %>% filter(is.finite(lat), is.finite(lon)) %>%
    group_by(site) %>% summarise(lat0 = median(lat), lon0 = median(lon), .groups="drop")
  idx <- which.min(haversine_km(site_centers$lat0, site_centers$lon0, target_lat, target_lon))
  sel_site <- site_centers$site[idx]
} else sel_site <- "Sudbury Reef"

d <- dat %>% filter(site == sel_site, datetime >= t_start, datetime <= t_end)
if (!all(is.na(d$variable))) d <- d %>% filter(variable %in% c("salt","salinity","sal"))

# ---- AGGREGATE & MAP DEPTHS ----
ds <- d %>% filter(is.finite(datetime), is.finite(depth_m), is.finite(value)) %>%
  group_by(datetime, depth_m) %>% summarise(salinity = mean(value, na.rm=TRUE), .groups="drop")
if (nrow(ds) == 0) stop("No Sudbury Reef salinity rows in the requested window after parsing.")

avail <- sort(unique(ds$depth_m)); stopifnot(length(avail) > 0)
mapped <- map_unique(avail, depths_req); names(mapped) <- depths_req; mapped <- mapped[!is.na(mapped)]

tol <- 1e-6
df_or <- bind_rows(lapply(names(mapped), function(d_req){
  d_av <- mapped[[d_req]]
  ds %>% filter(abs(depth_m - d_av) < tol) %>% mutate(depth_m = as.numeric(d_req))
}))
stopifnot(nrow(df_or) > 0)

# ---- PLOT (same aesthetics) ----
Sudbury_2023 <- ggplot(df_or, aes(x = datetime, y = salinity, colour = factor(depth_m))) +
  geom_line(alpha = 0.9) +
  scale_x_datetime(
    breaks = seq(as.POSIXct("2023-12-13 00:00:00", tz = "UTC"),
                 as.POSIXct("2023-12-30 00:00:00", tz = "UTC"), by = "1 day"),
    date_labels = "%d-%b"
  ) +
  scale_y_continuous(limits = c(20, 36), breaks = seq(20, 36, by = 1)) +
  theme_bw(base_size = 13, base_family = "Times New Roman") +
  labs(x = "Date (Feb 15thâMar 28th 2011, UTC)", y = "Salinity (PSU)", colour = "Depth (m)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), panel.grid = element_blank())

Sudbury_2023
# ggsave("Sudbury Reef_FebMar2011.png", OtterReef_FebMar2011, width = 12, height = 6, dpi = 300)
```

###### Data summaries RM-TWSville_Dec 2023_1
```{r}
# 1) Load and clean
path <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/Figures/Mulgrave - Twnsville/Time series/Data/2509.869f63b-collected.csv'
df <- read_csv(path, show_col_types = FALSE)

# 2) Robust datetime + numeric coercions
tmp <- gsub("T", " ", df$`Aggregated Date/Time`)
dt  <- as.POSIXct(tmp, format = "%Y-%m-%d %H:%M:%S", tz = "UTC")
na  <- is.na(dt)
dt[na] <- as.POSIXct(tmp[na], format = "%Y-%m-%d %H:%M", tz = "UTC")

df <- df %>%
  mutate(
    `Aggregated Date/Time` = dt,
    date_only = as_date(`Aggregated Date/Time`),
    lowest = readr::parse_number(as.character(lowest)),
    Depth  = readr::parse_number(as.character(Depth))
  ) %>%
  filter(!is.na(`Aggregated Date/Time`))

# 3) Restrict to 13â30 Dec of the dataset year
yr <- year(min(df$`Aggregated Date/Time`, na.rm = TRUE))
win <- df %>%
  filter(
    `Aggregated Date/Time` >= ymd_hms(sprintf("%d-12-13 00:00:00", yr), tz = "UTC"),
    `Aggregated Date/Time` <= ymd_hms(sprintf("%d-12-30 23:59:59", yr), tz = "UTC")
  )

# 4) Minimum salinity (value + timestamp) per site/depth
min_sal <- win %>%
  filter(!is.na(lowest)) %>%
  arrange(`Site Name`, Depth, lowest, `Aggregated Date/Time`) %>%
  group_by(`Site Name`, Depth) %>%
  slice(1) %>%
  ungroup() %>%
  transmute(`Site Name`, Depth, `Min Salinity` = lowest, Date = `Aggregated Date/Time`)

# 5) Unique calendar days with salinity < 30 PSU per site/depth
below_30 <- win %>%
  filter(!is.na(lowest), lowest < 30) %>%
  group_by(`Site Name`, Depth) %>%
  summarise(
    Days_below_30 = n_distinct(date_only),
    Dates = list(sort(unique(date_only))),
    .groups = "drop"
  )

# 6) Joined summary
summary_tbl <- min_sal %>%
  left_join(below_30, by = c("Site Name", "Depth")) %>%
  arrange(`Site Name`, Depth)

# 7) Inspect
print(head(min_sal, 10))
print(head(below_30, 10))
print(head(summary_tbl, 10))
```

###### Data summaries RM-TWSville_Dec 2023_2
```{r}
# 1) Load and clean
path <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/Figures/Mulgrave - Twnsville/Time series/Data/2509.e875848-collected.csv'
df <- read_csv(path, show_col_types = FALSE)

# 2) Robust datetime + numeric coercions
tmp <- gsub("T", " ", df$`Aggregated Date/Time`)
dt  <- as.POSIXct(tmp, format = "%Y-%m-%d %H:%M:%S", tz = "UTC")
na  <- is.na(dt)
dt[na] <- as.POSIXct(tmp[na], format = "%Y-%m-%d %H:%M", tz = "UTC")

df <- df %>%
  mutate(
    `Aggregated Date/Time` = dt,
    date_only = as_date(`Aggregated Date/Time`),
    lowest = readr::parse_number(as.character(lowest)),
    Depth  = readr::parse_number(as.character(Depth))
  ) %>%
  filter(!is.na(`Aggregated Date/Time`))

# 3) Restrict to 13â30 Dec of the dataset year
yr <- year(min(df$`Aggregated Date/Time`, na.rm = TRUE))
win <- df %>%
  filter(
    `Aggregated Date/Time` >= ymd_hms(sprintf("%d-12-13 00:00:00", yr), tz = "UTC"),
    `Aggregated Date/Time` <= ymd_hms(sprintf("%d-12-30 23:59:59", yr), tz = "UTC")
  )

# 4) Minimum salinity (value + timestamp) per site/depth
min_sal <- win %>%
  filter(!is.na(lowest)) %>%
  arrange(`Site Name`, Depth, lowest, `Aggregated Date/Time`) %>%
  group_by(`Site Name`, Depth) %>%
  slice(1) %>%
  ungroup() %>%
  transmute(`Site Name`, Depth, `Min Salinity` = lowest, Date = `Aggregated Date/Time`)

# 5) Unique calendar days with salinity < 30 PSU per site/depth
below_30 <- win %>%
  filter(!is.na(lowest), lowest < 30) %>%
  group_by(`Site Name`, Depth) %>%
  summarise(
    Days_below_30 = n_distinct(date_only),
    Dates = list(sort(unique(date_only))),
    .groups = "drop"
  )

# 6) Joined summary
summary_tbl <- min_sal %>%
  left_join(below_30, by = c("Site Name", "Depth")) %>%
  arrange(`Site Name`, Depth)

# 7) Inspect
print(head(min_sal, 10))
print(head(below_30, 10))
print(head(summary_tbl, 10))
```


###### Data summaries RM-TWSville_Dec 2023_3
```{r}
# 1) Load and clean
path <- '/Users/mcle5454/Desktop/For submission/Brach project 1/Review - major revisions/Characterising salinity in plumes/Dec 2023/2509.ef4e051-collected.csv'
df <- read_csv(path, show_col_types = FALSE)

# 2) Robust datetime + numeric coercions
tmp <- gsub("T", " ", df$`Aggregated Date/Time`)
dt  <- as.POSIXct(tmp, format = "%Y-%m-%d %H:%M:%S", tz = "UTC")
na  <- is.na(dt)
dt[na] <- as.POSIXct(tmp[na], format = "%Y-%m-%d %H:%M", tz = "UTC")

df <- df %>%
  mutate(
    `Aggregated Date/Time` = dt,
    date_only = as_date(`Aggregated Date/Time`),
    lowest = readr::parse_number(as.character(lowest)),
    Depth  = readr::parse_number(as.character(Depth))
  ) %>%
  filter(!is.na(`Aggregated Date/Time`))

# 3) Restrict to 13â30 Dec of the dataset year
yr <- year(min(df$`Aggregated Date/Time`, na.rm = TRUE))
win <- df %>%
  filter(
    `Aggregated Date/Time` >= ymd_hms(sprintf("%d-12-13 00:00:00", yr), tz = "UTC"),
    `Aggregated Date/Time` <= ymd_hms(sprintf("%d-12-30 23:59:59", yr), tz = "UTC")
  )

# 4) Minimum salinity (value + timestamp) per site/depth
min_sal <- win %>%
  filter(!is.na(lowest)) %>%
  arrange(`Site Name`, Depth, lowest, `Aggregated Date/Time`) %>%
  group_by(`Site Name`, Depth) %>%
  slice(1) %>%
  ungroup() %>%
  transmute(`Site Name`, Depth, `Min Salinity` = lowest, Date = `Aggregated Date/Time`)

# 5) Unique calendar days with salinity < 30 PSU per site/depth
below_30 <- win %>%
  filter(!is.na(lowest), lowest < 30) %>%
  group_by(`Site Name`, Depth) %>%
  summarise(
    Days_below_30 = n_distinct(date_only),
    Dates = list(sort(unique(date_only))),
    .groups = "drop"
  )

# 6) Joined summary
summary_tbl <- min_sal %>%
  left_join(below_30, by = c("Site Name", "Depth")) %>%
  arrange(`Site Name`, Depth)

# 7) Inspect
print(head(min_sal, 10))
print(head(below_30, 10))
print(head(summary_tbl, 10))
```

