0

I am trying to perform PCA on MODIS imagery. I got my code to run using MODIS (it also worked for Landsat), but when I mask my MODIS collection the PCA fails. This had to do with missing values, as GEE doesn't run on sparse matrices.

Question: How do I mask bad pixels during a PCA? One can not mask before the PCA, so how can I mask pixels after PCA?

Here is my code. Extent is my ROI.

var dataset2019 = ee.ImageCollection('MODIS/006/MCD43A4').filter(ee.Filter.date('2018-12-01', '2019-04-20'));
var dataset2020 = ee.ImageCollection('MODIS/006/MCD43A4').filter(ee.Filter.date('2019-12-01', '2020-04-20'));
var modisCol = dataset2019.merge(dataset2020);

// Create a QA mask function
var filterqa = function(image){ 
  var mask1 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band1").eq(0);
  var mask2 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band2").eq(0);
  var mask3 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band3").eq(0);
  var mask4 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band4").eq(0);
  var mask5 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band5").eq(0);
  var mask6 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band6").eq(0);
  var mask7 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band7").eq(0);
  return image.updateMask(mask1).updateMask(mask2).updateMask(mask3).updateMask(mask4)
  .updateMask(mask5).updateMask(mask6).updateMask(mask7);
};

// Preparing imagery for PCA
var PCA = function(image){
  var image = image.select(['Nadir_Reflectance_Band1','Nadir_Reflectance_Band2', 
        'Nadir_Reflectance_Band3','Nadir_Reflectance_Band4',
        'Nadir_Reflectance_Band5','Nadir_Reflectance_Band6',
        'Nadir_Reflectance_Band7']);
  var orig = image;
  var region = Extent;
  var scale = 500;
  var bandNames = ['Nadir_Reflectance_Band1','Nadir_Reflectance_Band2',
  'Nadir_Reflectance_Band3','Nadir_Reflectance_Band4','Nadir_Reflectance_Band5',
  'Nadir_Reflectance_Band6','Nadir_Reflectance_Band7'];
  var meanDict = image.reduceRegion({
    reducer: ee.Reducer.mean(),
    geometry: region,
    scale: scale,
    maxPixels: 1e9
  });
  var means = ee.Image.constant(meanDict.values(bandNames));
  var centered = image.subtract(means);
  var getNewBandNames = function(prefix) {
  var seq = ee.List.sequence(1, 7);
  return seq.map(function(b) {
    return ee.String(prefix).cat(ee.Number(b).int());
    });
  };
  // PCA function
  var getPrincipalComponents = function(centered, scale, region) {
    var arrays = centered.toArray();
    var covar = arrays.reduceRegion({
      reducer: ee.Reducer.centeredCovariance(),
      geometry: region,
      scale: scale,
      maxPixels: 1e9
    });
    var covarArray = ee.Array(covar.get('array'));
    var eigens = covarArray.eigen();
    var eigenValues = eigens.slice(1, 0, 1);
    var eigenVectors = eigens.slice(1, 1);
    var arrayImage = arrays.toArray(1);
    var principalComponents = ee.Image(eigenVectors).matrixMultiply(arrayImage);
    var sdImage = ee.Image(eigenValues.sqrt())
    .arrayProject([0]).arrayFlatten([getNewBandNames('sd')]);
    return principalComponents.arrayProject([0])
    .arrayFlatten([getNewBandNames('pc')])
    .divide(sdImage);
    };
  var pcImage = getPrincipalComponents(centered, scale, region);
  return ee.Image(image.addBands(pcImage));
};

var modisColClip = modisCol
.map(function(image){return(image.clip(Extent))})
.map(filterqa)
.map(PCA)
.sort('system:time_start');
print("modis",modisColClip);

This code produces a error due to the masking of pixels. How can I change this? I have tried to swap around the order of functions to map, but I don't want to conduct a PCA using QA bands, and if I ignore the QA bands, the filter function fails because there is no QA bands.

Any idea?

1 Answer 1

0

One needs to perform a Join to mask pixel after performing PCA. Basically, after performing PCA, you have to Join it with the original imagery that contained the QA bands (modisCol).

// Compile PCA collection
var PCAColClip = modisCol
.map(function(image){return(image.clip(Extent))})
.map(PCA)
.sort('system:time_start')
.select(['pc1']);
print("modis PCA",PCAColClip);

// Mask PCA using original QA bands
var mod1 = modisCol.map(function(image){return(image.clip(Extent))});
var mod2 = PCAColClip;
var filter = ee.Filter.equals({
  leftField: 'system:time_start',
  rightField: 'system:time_start'
});
var simpleJoin = ee.Join.inner();
var innerJoin = ee.ImageCollection(simpleJoin.apply(mod1, mod2, filter));
var joined = innerJoin.map(function(feature) {
  return ee.Image.cat(feature.get('primary'), feature.get('secondary'));
});
// Compile PCA collection
var PCAMasked = joined
.map(filterqa)
.select(['pc1']);
print("modis PCA Masked",PCAMasked);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.