https://doi.org/10.5281/zenodo.15690037
UV_Slope.R
# This function is called by Run.R to calculate the Negative_uv_slope flag on GLORIA Rrs spectra
# Refer to README.md for a description of the method.
slope_uv = function(gloria_rrs) {
# Detect decreasing Rrs from 350-420 nm
# Return a logical vector with 1 where slope is more negative than the
# threshold, and the actual slope value.
# threshold = -0.005;
rrs = gloria_rrs[,-1]
WAVE = c(350:900)
#Min and Median spectra functions without NA
std.na = function(x) {return(sd(x, na.rm = T))}
mean.na = function(x) { return(mean(x, na.rm = T))}
STD.data = apply(rrs, MARGIN = 1, std.na)
MEAN.data = apply(rrs, MARGIN = 1, mean.na)
rrs.normalized = (rrs-MEAN.data)/STD.data
threshold = -0.005
LIMITS = c(350:420)
select_Rrs = select(rrs.normalized,
paste('Rrs_', 350:420, sep = ''))
SLOPE = data.frame(GLORIA_ID = gloria_rrs$GLORIA_ID,
SLOPE = 0,
flag = 0)
for(i in 1:nrow(SLOPE)) {
DF = data.frame(WV = c(350:900),
Rrs = t(rrs.normalized[i,])) %>% na.omit()
DF.filter = filter(DF, WV >= min(LIMITS) & WV <= max(LIMITS)) %>% na.omit()
names(DF.filter)[2] = 'Rrs'
if(nrow(DF.filter) != length(LIMITS)) {
SLOPE$flag[i] = NaN
print('Size Different from limits. Not accounting for noisy')
}
if(nrow(DF.filter) == length(LIMITS)) {
MODEL = lm(Rrs~WV, data = DF.filter)
SLOPE$SLOPE[i] = MODEL$coefficients[2]
print('Slope Calculated')
}
}
SLOPE[SLOPE$SLOPE < threshold, 'flag'] = 1
return(SLOPE[,c('GLORIA_ID', 'flag')])
}
noise_red_edge = function(gloria_rrs) {
rrs = gloria_rrs[,-1]
WAVE = c(400:900)
#Min and Median spectra functions without NA
std.na = function(x) {return(sd(x, na.rm = T))}
mean.na = function(x) { return(mean(x, na.rm = T))}
STD.data = apply(rrs, MARGIN = 1, std.na)
MEAN.data = apply(rrs, MARGIN = 1, mean.na)
rrs.normalized = (rrs-MEAN.data)/STD.data
red_edge_limits = c(750:900)
qc_flag_noisy_rededge = data.frame(GLORIA_ID = gloria_rrs$GLORIA_ID,
RMSE = 0,
flag = 0)
#Threshold
noise_thresh = 0.2
for(i in 1:nrow(qc_flag_noisy_rededge)) {
DF = data.frame(WV = c(400:900),
Rrs = t(rrs.normalized[i,])) %>% na.omit()
DF.filter = filter(DF, WV > 749 & WV < 901) %>% na.omit()
if(nrow(DF.filter) != length(red_edge_limits)) {
qc_flag_noisy_rededge$flag[i] = NaN
print('Size Different from limits. Not accounting for noisy')
}
if(nrow(DF.filter) == length(red_edge_limits)) {
POLY = polyFit(xy = DF.filter, deg = 4)
PREDICTION = predict(POLY, newdata = DF.filter$WV)
qc_flag_noisy_rededge$RMSE[i] = rmse(actual = DF.filter[,2],
predicted = PREDICTION)
print('RMSE Calculated')
}
}
qc_flag_noisy_rededge[qc_flag_noisy_rededge$RMSE > noise_thresh, 'flag'] = 1
return(qc_flag_noisy_rededge[,c('GLORIA_ID', 'flag')])
}
noise_uv_edge = function(gloria_rrs) {
rrs = gloria_rrs[,-1]
WAVE = c(350:900)
#Min and Median spectra functions without NA
std.na = function(x) {return(sd(x, na.rm = T))}
mean.na = function(x) { return(mean(x, na.rm = T))}
STD.data = apply(rrs, MARGIN = 1, std.na)
MEAN.data = apply(rrs, MARGIN = 1, mean.na)
rrs.normalized = (rrs-MEAN.data)/STD.data
uv_limits = c(350:400)
qc_flag_noisy_UV = data.frame(GLORIA_ID = gloria_rrs$GLORIA_ID,
RMSE = 0,
flag = 0)
#Threshold
noise_thresh = 0.15
for(i in 1:nrow(qc_flag_noisy_UV)) {
DF = data.frame(WV = c(350:900),
Rrs = t(rrs.normalized[i,])) %>% na.omit()
DF.filter = filter(DF, WV >= min(uv_limits) & WV <= max(uv_limits)) %>% na.omit()
LIMITS = rbind(min = filter(DF.filter, WV == 350),
max = filter(DF.filter, WV == 400)) %>% dim()
if(LIMITS[1] != 2) {
qc_flag_noisy_UV$flag[i] = NaN
print('Size Different from limits. Not accounting for noisy')
}
if(LIMITS[1] == 2) {
POLY = polyFit(xy = DF.filter, deg = 4)
PREDICTION = predict(POLY, newdata = DF.filter$WV)
qc_flag_noisy_UV$RMSE[i] = rmse(actual = DF.filter[,2],
predicted = PREDICTION)
print('RMSE Calculated')
}
}
qc_flag_noisy_UV[qc_flag_noisy_UV$RMSE > noise_thresh, 'flag'] = 1
return(qc_flag_noisy_UV[,c('GLORIA_ID', 'flag')])
}