Composit and Mask Satellite Data in R

There are several challenges when using satellite data across large areas: multiple tiles or “scenes” make up the study area, and clouds, haze, and other atmospheric factors will render pixels unusable. The following steps apply a series of masking, mosaicing, and merging to convert multiple scenes to seamless, usable, imagery.

Prelims

library(sf)
library(raster)

View Individual Images

Note clouds and haze in the southern and eastern parts of these images respectively.

img1<-raster::stack("images/1852708_1653019_2018-11-16_101e_BGRN_SR.tif")
img2<-raster::stack("images/c1852708_1653119_2018-11-16_101e_BGRN_SR.tif")

plotRGB(img1, stretch="lin")
plotRGB(img2, stretch="lin")

View Mask

The first layer of this (planet labs) mask includes “total usable pixels” which is an aggrigate of clouds, haze, and snow. 1 is a clear pixel and 0 is not.

mask1<-raster::stack("masks/1852708_1653019_2018-11-16_101e_udm2.tif")
mask2<-raster::stack("masks/1852708_1653119_2018-11-16_101e_udm2.tif")

plot(mask1[[1]])
plot(mask2[[1]])

The following function helps mosaic, merge, and reclassify values of a list of rasters.

MosaicReclassRaster <- function(Location, # Location of raster (.tif) files
                                Mosaic = "Y", #mosic(Y) or merge(N)
                                bands = FALSE, #Select band, if false all bands returned
                                Reclass0toNA=FALSE){ #If TRUE, converts 0 values to NA
  
current.list <- list.files(path=paste(Location), full.names=TRUE, pattern = ".tif")
  
   #read in raster files as a raster stack
  raster.list <- lapply(current.list, raster::stack)
  
if (bands > 0) {
  #select band if interested in only 1
  raster.list <- lapply(current.list, raster, band=bands)
  
} else{}
  
if (Reclass0toNA==TRUE) {
  #reclassify 0 (unusable pixels) as NA
  reclass_na <- matrix(c(0, NA),
                ncol = 2,
                byrow = TRUE) #reclass matrix
      raster.list <- lapply(raster.list, raster::reclassify, reclass_na)
      
} else {}
  
 if (Mosaic == "Y"){
  #if Y, average any overlapping pixels and mosaic, ignore NA
  raster.list$fun <- mean
    Mosaic <- do.call( raster::mosaic, raster.list)
    
 } else {
    #if N, take value in first raster in the list (instead of average) unless it is NA
    Merge <- do.call( raster::merge, raster.list)
 }
}

Composite Images

Here we can see images are merged by selecting “N” in the function. This means it selects the first pixel in the list of rasters, unless it is NA, in which case it moves on until a suitable pixel is found, if no non NAs are found NA is returned. This is why we now have white space in the image where black used to be 0 and was reclassified to NA.

Note that clouds and haze are still present

CompositeImage<-MosaicReclassRaster("images", 
                                  "N", 
                                  bands = FALSE, 
                                  Reclass0toNA = TRUE)

plotRGB(CompositeImage, stretch="lin")

Mosaic Mask

After masking, clouds are now simply NA, and will not confound the remainder of the analysis

CompositeMask<-MosaicReclassRaster("masks", 
                                  "N", 
                                  bands = 1, 
                                  Reclass0toNA = TRUE)

### And Mask Raster
masked_composite<-mask(CompositeImage,
                       CompositeMask)

plotRGB(masked_composite, stretch="lin")