4 Rain visualisation

The previous approaches to segmentation have perhaps, unintentionally, resulted in a methodology to thoroughly clean RBC PPIs from rain, but this is not necessarily useful for visualisation. Now, instead, we’ll try an approach that simply focuses on visually distinguishing rain from other sources of reflectivity in the RBC PPIs.

Let’s plot a RBC PPI.

pvolfile <- "data/20201002/NLHRW_pvol_20201002T1205_6356.h5"
# pvolfile <- "data/20201001/NLHRW_pvol_20201001T2040_6356.h5"
pvolfile <- "data/20201001/NLHRW_pvol_20201001T1740_6356.h5"

pvol <- read_pvolfile(file = pvolfile, param = "all")
pvol <- calculate_param(pvol, 
                        ZDRL = 10 ** ((DBZH - DBZV) /10),
                        DPR = 10 * log10((ZDRL + 1 - 2 * ZDRL^0.5 * RHOHV) / (ZDRL + 1 + 2 * ZDRL^ 0.5 * RHOHV)))
vp <- calculate_vp(pvolfile, verbose = FALSE)
ppi_rainy <- integrate_to_ppi(pvol, vp, xlim = c(-180000, 180000), ylim = c(-180000, 180000), res = 500, param = "DBZH")
plot(ppi_rainy)

And DBZH and DPR across 5 scans.

pvol$scans <- lapply(pvol$scans, function(x) {
  x$params[["DBZH"]][is.na(x$params[["DPR"]])] <- NA
  return(x)
})
lapply(pvol$scans[1:5], function(x) {
  dbzh <- get_param(x, "DBZH")
  dpr <- get_param(x, "DPR")
  vradh <- get_param(x, "VRADH")
  ppidbzh <- project_as_ppi(dbzh, grid_size = 500, range_max = 150000)
  ppidpr <- project_as_ppi(dpr, grid_size = 500, range_max = 150000)
  # ppivradh <- project_as_ppi(vradh, grid_size = 500, range_max = 150000)
  plot(ppidbzh) + plot(ppidpr) # + plot(ppivradh)
})

## [[1]]
## 
## [[2]]
## 
## [[3]]
## 
## [[4]]
## 
## [[5]]

Now we can use DPR to visualize where rain is.

masks <- lapply(pvol$scans[1:15], function(x) {
  ppi <- project_as_ppi(get_param(x, "DPR"), grid_size = 500, range_max = 180000)
  
  if (ppi$geo$elangle < 90) {
    dpr <- as.cimg(as.matrix(ppi$data))
    (dpr <= -12 & !is.na(dpr)) %>%
      clean(4) %>%  # Remove speckles by shrinking then growing using a 4px radius
      fill(10) -> filled
    
    if (sum(filled) > 0) {
      filled %>%
        split_connected() %>%  # Split image in contiguous areas classified as rain
        purrr::keep(~ sum(.) > 100) %>%  # Only keep contiguous rain areas if area is > 100 pixels
        parany() -> contiguous  # Merge to 1 image again
    }
    if (exists("contiguous") && !is.null(contiguous)) {  # Only buffer if any rain areas of > 50 pixels are retained
      contiguous %>%
        distance_transform(1, 2) %>%  # Calculate Euclidean distance (2nd argument) to pixels classified as 1
        threshold(5) -> dpr_mask
      
      dpr_mask %>%
        distance_transform(1, 2) -> dist_mask
      
      dpr_mask <- -dpr_mask
      
      m <- as.matrix(dpr_mask)
      m[m == 0] <- 1
      m[m == -1] <- NA
      
      d <- as.matrix(dist_mask)
      d[d == 0] <- NA
      return(list(m, d))
    } else {
      o <- matrix(nrow = ppi$data@grid@cells.dim[1], ncol = ppi$data@grid@cells.dim[2])
      return(list(o, o))
    }
  }
})

rain_elevations <- simplify2array(lapply(masks, function(x) x[[1]]))
rain_distances <- simplify2array(lapply(masks, function(x) x[[2]]))
rain_elevations <- apply(rain_elevations, c(1, 2), sum, na.rm = TRUE)
rain_elevations[rain_elevations <= 1] <- NA
rain_distances <- apply(rain_distances, c(1, 2), sum, na.rm = TRUE)
rain_distances[rain_distances == 0] <- NA

ppi_rainy$data$rain_distance <- as.vector(rain_distances)
ppi_rainy$data$rain_elevations <- as.vector(rain_elevations)

And plot the visualization.

data <- do.call(function(y) ppi_rainy$data[y], list(c("VIR", "rain_elevations")))
data <- raster::as.data.frame(stack(data), xy = T)
bbox <- coord_fixed(xlim = c(-160000, 160000), ylim = c(-160000, 160000))
zlim = c(0, 5000)
index <- which(data[, 3] < zlim[1])
if (length(index) > 0) {
  data[index, 3] <- zlim[1]
}
index <- which(data[, 3] > zlim[2])
if (length(index) > 0) {
  data[index, 3] <- zlim[2]
}

ggplot(data = data) +
  geom_raster(aes(x = x, y = y, fill = VIR)) +
  scale_fill_viridis_c(trans = "log10", na.value = "transparent", option = "plasma") +
  new_scale_fill() +
  geom_raster(aes(x = x, y = y, fill = rain_elevations, alpha = rain_elevations)) +
  # scale_fill_continuous(na.value = "transparent") +
  scale_fill_gradient(na.value = "transparent", low = "white", high = "#7fb4ff") +
  scale_alpha_continuous(range = c(0.9, 1)) +
  theme_bw() +
  bbox
## Warning: Transformation introduced infinite values in discrete y-axis
source("R/comp_ppi.R")
hrw_ppis <- Sys.glob(file.path("data/processed/final-ppis", "*NL62*"))
dhl_ppis <- Sys.glob(file.path("data/processed/final-ppis", "*NL61*"))

generate_composites <- function(hrw_ppis, dhl_ppis, res, maxrange) {
  # Make a new empty PPI to store all composites in
  template_ppi <- readRDS(hrw_ppis[1])
  all <- template_ppi$data@data %>%
    filter(row_number() == 0)
  
  basemap <- NULL
  
  for (i in seq_along(hrw_ppis)) {
    ppi_hrw <- readRDS(hrw_ppis[i])
    ppi_dhl <- readRDS(dhl_ppis[i])
    
    # Set all columns to NA if further than maxrange from radar
    ppi_hrw$data@data[ppi_hrw$data@data$dist_radar > maxrange, ] <- NA
    ppi_dhl$data@data[ppi_dhl$data@data$dist_radar > maxrange, ] <- NA
    
    params <- c("VIR", "VID", "R", "overlap", "eta_sum", "eta_sum_expected", "dist_radar", "class",
                "urban", "agricultural", "semiopen", "forests", "wetlands", "waterbodies", "dist_urban", "human_pop",
                "wb_area_id", "wb_area_nr", "ptt_route", "wb_area_ha", "wb_total_biomass", "ptt_total_biomass", "total_biomass", 
                "wb_weighted_mean_weight", "ptt_weighted_mean_weight", "weighted_mean_weight")
    # All mean methods except for factors and urban area (set to max), because we want to strictly filter out fireworks
    methods <- c("mean", "mean", "mean", "mean", "mean", "mean", "min", "min",
                 "max", "mean", "mean", "mean", "mean", "mean", "mean", "mean", 
                 "factor", "factor", "factor", "factor", "mean", "mean", "mean",
                 "mean", "mean", "mean")
    cppi <- comp_ppi(list(ppi_hrw, ppi_dhl), param = params, method = methods, res = c(res, res), coverage = "count")
    
    # Set rain and background pixels to NA
    cppi$data$VIR[cppi$data$class < 2] <- NA
    
    # Add coordinates
    coords_cppi <- raster::coordinates(cppi$data)
    cppi$data$x <- coords_cppi[, 1]
    cppi$data$y <- coords_cppi[, 2]
    
    # Add pixel ID
    cppi$data@data %>%
      mutate(pixel = row_number()) -> cppi$data@data
    
    # Solve different factors
    solve_factors <- function(x) {
      r <- x[1]
      if (is.na(x[1]) && is.na(x[2])) { r <- NA }
      if (is.na(x[1]) && !is.na(x[2])) { r <- x[2] }
      if (!is.na(x[1]) && is.na(x[2])) { r <- x[1] }
      if (!is.na(x[1]) && !is.na(x[2])) {
        if (x[1] != x[2]) {
          r <- NA
        } else {
          r <- x[1]
        }
      }
      r
    }
    
    for (j in which(methods == "factor")) {
      cppi$data@data[params[j]] <- apply(cppi$data@data[params[j]], 1, solve_factors)
    }
    
    saveRDS(cppi, file = paste("data/processed/composite-ppis/", res, "m/", strftime(ppi_hrw$datetime, format = "%Y%m%d%H%M"), ".RDS", sep = ""))
    
    cppi$data@data %>%
      mutate(datetime = as.POSIXct(ppi_hrw$datetime)) %>%
      bind_rows(all) -> all
    
    if (i == 1) {
      basemap <- download_basemap(cppi, alpha = 0.3)
    }
    
    cppi$data$VIR <- log10(cppi$data$VIR)
    cppi$data$VIR[is.na(cppi$data$VIR)] <- 0
    
    bioRad::map(cppi, map = basemap, radar_size = 1, xlim = c(3.1, 6.8), ylim = c(51, 54), zlim = c(0, 4.5),
                palette = viridis(256, option = "viridis", alpha = 0.6)) +
      labs(title = "Fireworks NYE 2017-2018",
           subtitle = paste(ppi_hrw$datetime, ' UTC', sep = ""))

    ggsave(paste("data/plots/vir-ppis/", res, "m/", strftime(ppi_hrw$datetime, format = "%Y%m%d%H%M"), ".png", sep = ""))
  }
  
  saveRDS(all, file = paste("data/processed/all_", res, "m.RDS", sep = ""))
}

# Somehow this often hangs despite ample memory available, in which case better executed serially.
# r <- parallel::mclapply(c(500, 1000, 2000), function(x) { generate_composites(hrw_ppis, dhl_ppis, res = x, maxrange = 66000)},
#                         mc.cores = 3, mc.preschedule = FALSE)
r <- parallel::mclapply(c(1000, 2000), function(x) { generate_composites(hrw_ppis, dhl_ppis, res = x, maxrange = 66000)},
                        mc.cores = 3, mc.preschedule = FALSE)