ArcGIS Desktop – Mapping Data Greater Than 1 Standard Deviation from the Mean

arcgis-desktopmappingraster

I have several geoTIFF files generated by Google Earth Engine (GEE) from Landsat 8 scenes. Each file covers 100Km^2, with each pixel covering 30m^2 and containing a calculated value of Land Surface Temperature (LST).

I'm using ArcGIS Pro, and although I can map these using basic symbology I can't work out how to filter the raster data so that I can map only values that are 1 or more standard deviations from the mean. (There may be a tool that could do something similar/filter the map itself).

Ideally, I would be able to batch process images to map these data, if possible adding in a vector mask exported from GEE, too.

I have had a good look online, but the closest I've found are solutions using the Pixel Editor to correct errors in terrain elevation.

I'm not sure where to start.

Best Answer

I did this in R in the end.

My code uses a shapefile mask to crop the extent of the image, and then uses it as an overlay to aid image analysis.

pre/post refer to before/after construction in the aoi.

# Set directory for geoTIFFs
pre_tif_Dir <- "/..."
post_tif_Dir <- "..." 

# Create filename list
pre_fileNames <- list.files(pre_tif_Dir,full.names=TRUE,pattern="tif$")
post_fileNames <- list.files(post_tif_Dir,full.names=TRUE,pattern="tif$")

# Import mask shapefile
mask <- shapefile(".../mask.shp")

## Create a stack (files don't yet have same crs so can't use brick)
pre_stk <- stack(as.list(pre_fileNames))
post_stk <- stack(as.list(post_fileNames))

# Check crs
compareCRS(mask,pre_stk) 
compareCRS(mask,post_stk) 

# Correct crs if needed
pre_stk = projectRaster(pre_stk, crs=crs(mask))
post_stk = projectRaster(post_stk, crs=crs(mask))

## Check crs
extent(pre_stk)
extent(post_stk)
extent(mask)

# Crop (crop first since it will make masking quicker)
pre_stk_crop <- crop(pre_stk,extent(mask)) 
post_stk_crop <- crop(post_stk,extent(mask)) 

# Mask if needed
# pre-stk <- mask(pre_stk, mask, inverse = F)
# post-stk <- mask(post_stk, mask, inverse = F)

## look at extents to check
extent(pre_stk_crop)
extent(post_stk_crop)
extent(mask)

psc <- pre_stk_crop
posc <- post_stk_crop

# Loop to calculate mean and standard deviation
for(i in 1:nlayers(psc)){
  v <- values(psc[[i]]) ## get values from raster
  m <- mean(v,na.rm=TRUE)
  s <- sd(v,na.rm=TRUE)
  v[ ((m-s)<=v) & ((m+s)>=v) ] <- NA # change to plot different extent around mean.
  values(psc[[i]]) <- v  # Update values in raster
}

for(i in 1:nlayers(posc)){
  v <- values(posc[[i]]) ## get values from raster
  m <- mean(v,na.rm=TRUE)
  s <- sd(v,na.rm=TRUE)
  v[ ((m-s)<=v) & ((m+s)>=v) ] <- NA
  values(posc[[i]]) <- v  
}

# Create palette for thermal plot
tempcol <- colorRampPalette(c("purple", "blue", "skyblue", "green", "lightgreen", "yellow", "orange", "red", "darkred"))

# Test plot
extent(psc[[1]])
plot(psc[[1]], col=tempcol(100)) # Color palette varies by image, but red it always hotter
plot(mask, lwd=0.2, add=TRUE) # adds plot of mask shapefile

# Create grid for plots
par(mfrow = c(3,4)) 
par(mar = c(2,3,2,3))

# Loop to plot extreme values +/- 1 StdDev or greater from mean, overlaying vector of masked buffers
for(p in 1:nlayers(psc)){
  # plot(R[[p]], col=tempcol(100), zlim=c(0, 60)) # zlim() fixes LST scale and palette across images
  plot(psc[[p]], col=tempcol(100)) # Color palette varies by image, but red it always hotter
  plot(mask, lwd=0.2, add=TRUE) # adds plot of mask shapefile
  
}

for(p in 1:nlayers(posc)){
  # plot(R[[p]], col=tempcol(100), zlim=c(0, 60)) # Fixes LST scale and palette across images
  plot(posc[[p]], col=tempcol(100))
  plot(mask, lwd=0.2, add=TRUE)
  
}

I didn't write these to a raster (or series of raster), but it would be something like:

writeRaster(pre_stk,paste(names(pre_stk), "_Processed", sep=""), format="GTiff")

Related Question