library(sf)
## Warning: package 'sf' was built under R version 4.5.3
## Linking to GEOS 3.14.1, GDAL 3.12.1, PROJ 9.7.1; sf_use_s2() is TRUE
library(sp)
library(gstat)
## Warning: package 'gstat' was built under R version 4.5.3
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.5.3
library(metR)
library(terra)
## Warning: package 'terra' was built under R version 4.5.3
## terra 1.9.25
# Load the data
data <- read.csv("C:/Users/Aldrich Eva/Downloads/Bathymetric Data of Tikub.csv")
lake_shape <- st_read("C:/Users/Aldrich Eva/Downloads/Tikubshapefile.shp", quiet = TRUE)
# Fix lake geometry
sf_use_s2(FALSE)
## Spherical geometry (s2) switched off
lake_shape <- st_make_valid(lake_shape)
lake_shape <- st_union(lake_shape) |> st_as_sf()

if (is.na(st_crs(lake_shape))) {
  st_crs(lake_shape) <- 4326
}
# Preparing sampling points
data$Depth <- abs(data$Depth)

pts <- st_as_sf(
  data,
  coords = c("Longitude", "Latitude"),
  crs = st_crs(lake_shape)
)

pts <- st_intersection(pts, lake_shape)
## although coordinates are longitude/latitude, st_intersection assumes that they
## are planar
## Warning: attribute variables are assumed to be spatially constant throughout
## all geometries
# 4. Project to UTM Zone 51N
lake_utm <- st_transform(lake_shape, 32651)
pts_utm  <- st_transform(pts, 32651)

pts_sp <- as(pts_utm, "Spatial")
lake_v <- vect(lake_utm)
# 5. Create prediction grid
r <- rast(
  ext(lake_v),
  resolution = 3,
  crs = crs(lake_v)
)

grid <- as.data.frame(crds(r))
coordinates(grid) <- ~x + y
proj4string(grid) <- CRS(crs(lake_v))

# 6. Variogram and ordinary kriging
v <- variogram(Depth ~ 1, pts_sp)

depth_var <- var(data$Depth, na.rm = TRUE)

model <- vgm(
  psill = depth_var * 0.7,
  model = "Sph",
  nugget = depth_var * 0.08,
  range = 60
)

fit <- try(fit.variogram(v, model = model), silent = TRUE)

if (inherits(fit, "try-error") || any(is.na(fit$psill)) || any(is.na(fit$range))) {
  fit <- model
}

kr <- krige(Depth ~ 1, pts_sp, grid, model = fit)
## [using ordinary kriging]
# 7. Convert kriging output to raster and mask to lake
kr_df <- as.data.frame(kr)
names(kr_df)[1:2] <- c("x", "y")

r_pred <- rast(
  kr_df[, c("x", "y", "var1.pred")],
  type = "xyz",
  crs = crs(lake_v)
)

r_mask <- mask(r_pred, lake_v)

plot_df <- as.data.frame(r_mask, xy = TRUE, na.rm = TRUE)
names(plot_df)[3] <- "depth"

plot_df$depth[plot_df$depth < 0] <- 0

obs_max <- max(data$Depth, na.rm = TRUE)
plot_df$depth[plot_df$depth > obs_max] <- obs_max
# Create prediction grid
r <- rast(
  ext(lake_v),
  resolution = 3,
  crs = crs(lake_v)
)

grid <- as.data.frame(crds(r))
coordinates(grid) <- ~x + y
proj4string(grid) <- CRS(crs(lake_v))
# Variogram and ordinary kriging
v <- variogram(Depth ~ 1, pts_sp)

depth_var <- var(data$Depth, na.rm = TRUE)

model <- vgm(
  psill = depth_var * 0.7,
  model = "Sph",
  nugget = depth_var * 0.08,
  range = 60
)

fit <- try(fit.variogram(v, model = model), silent = TRUE)

if (inherits(fit, "try-error") || any(is.na(fit$psill)) || any(is.na(fit$range))) {
  fit <- model
}

kr <- krige(Depth ~ 1, pts_sp, grid, model = fit)
## [using ordinary kriging]
# Convert kriging output to raster and mask to lake
kr_df <- as.data.frame(kr)
names(kr_df)[1:2] <- c("x", "y")

r_pred <- rast(
  kr_df[, c("x", "y", "var1.pred")],
  type = "xyz",
  crs = crs(lake_v)
)

r_mask <- mask(r_pred, lake_v)

plot_df <- as.data.frame(r_mask, xy = TRUE, na.rm = TRUE)
names(plot_df)[3] <- "depth"

plot_df$depth[plot_df$depth < 0] <- 0

obs_max <- max(data$Depth, na.rm = TRUE)
plot_df$depth[plot_df$depth > obs_max] <- obs_max
# Contours
minor_breaks <- seq(0, floor(obs_max), by = 5)
major_breaks <- seq(0, floor(obs_max), by = 10)
# Plot
ggplot() +
  geom_tile(
    data = plot_df,
    aes(x = x, y = y, fill = depth),
    width = res(r_mask)[1],
    height = res(r_mask)[2]
  ) +
  geom_contour(
    data = plot_df,
    aes(x = x, y = y, z = depth),
    breaks = minor_breaks,
    color = "grey75",
    linewidth = 0.15
  ) +
  geom_contour(
    data = plot_df,
    aes(x = x, y = y, z = depth),
    breaks = major_breaks,
    color = "black",
    linewidth = 0.3
  ) +
  geom_text_contour(
    data = plot_df,
    aes(x = x, y = y, z = depth),
    breaks = minor_breaks,
    label.placer = label_placer_flattest(),
    size = 2.5,
    colour = "black",
    stroke = 0.1,
    check_overlap = TRUE
  ) +
  geom_sf(
    data = lake_utm,
    fill = NA,
    color = "black",
    linewidth = 0.5
  ) +
  scale_fill_gradientn(
    colours = c(
      "#d9f0ff",
      "#74c0fc",
      "#1c7ed6",
      "#0b3d91",
      "#002458",
      "#000525",
      "#000000"
    ),
    limits = c(0, obs_max),
    breaks = seq(0, floor(obs_max), by = 10),
    name = "depth (m)"
  ) +
  coord_sf(crs = st_crs(lake_utm), expand = FALSE) +
  theme_bw() +
  labs(x = NULL, y = NULL) +
  theme(
    panel.grid = element_blank(),
    aspect.ratio = 1,
    legend.position = "right"
  )