Google Earth Engine – Parsing MOD13Q1 QA Layer Using JavaScript API

google-earth-enginegoogle-earth-engine-javascript-apimodis

Working with Modis MOD13Q1 data and I am not sure how to correctly parse for pixel QA and DetailedQA.

// Call data 
var CRNP_Oban = ee.FeatureCollection("users/sijehasuk/CRNP_Oban2"),
    ndvi = ee.ImageCollection("MODIS/061/MOD13Q1");

// Explore the variable to view QA
print(ndvi);

// Create function with filter of quality pixels
var mask = function(im) {
  return im.updateMask(im.select("SummaryQA").eq(0));
};

Version VI SummaryQ includes bits 0-1 from the DetailedQA and I have masked Bit '0' (please, correct if done wrongly)
****Note: Bitmask for SummaryQA

/// Bits 0-1: VI quality (MODLAND QA Bits)
// 0: Good data, use with confidence
// 1: Marginal data, useful but look at detailed QA for more information
// 2: Pixel covered with snow/ice
// 3: Pixel is cloudy

// Apply filter to NDVI data
var ndvi = ee.ImageCollection(ndvi)
.filterDate('2000-01-01','2020-01-01')
.map(mask)
.select("NDVI")
.filterBounds(CRNP_Oban);

How do I include Bitmask 1 and apply other Bitmask for DetailedQA (see below) to ensure the best quality of pixel? See help2 Code and asset. While working on this in R, it was observed that the area had a lot of missing data issues. So it would be good to ease the QA a little so there is still some data left for analysis.

// VI quality indicators

// Bitmask for DetailedQA

// Bits 0-1: VI quality (MODLAND QA Bits)
// 0: VI produced with good quality
// 1: VI produced, but check other QA
// 2: Pixel produced, but most probably cloudy
// 3: Pixel not produced due to other reasons than clouds

// Bits 2-5: VI usefulness
// 0: Highest quality
// 1: Lower quality
// 2: Decreasing quality
// 4: Decreasing quality
// 8: Decreasing quality
// 9: Decreasing quality
// 10: Decreasing quality
// 12: Lowest quality
// 13: Quality so low that it is not useful
// 14: L1B data faulty
// 15: Not useful for any other reason/not processed

// Bits 6-7: Aerosol Quantity
// 0: Climatology
// 1: Low
// 2: Intermediate
// 3: High

// Bit 8: Adjacent cloud detected
// 0: No
// 1: Yes

// Bit 9: Atmosphere BRDF correction
// 0: No
// 1: Yes

// Bit 10: Mixed Clouds
// 0: No
// 1: Yes

// Bits 11-13: Land/water mask
// 0: Shallow ocean
// 1: Land (nothing else but land)
// 2: Ocean coastlines and lake shorelines
// 3: Shallow inland water
// 4: Ephemeral water
// 5: Deep inland water
// 6: Moderate or continental ocean
// 7: Deep ocean

// Bit 14: Possible snow/ice
// 0: No
// 1: Yes

// Bit 15: Possible shadow
// 0: No
// 1: Yes

See further description of the VI quality assessment.
Further description of VI QA Source

Best Answer

I'm unsure what values you wanted for each of the quality bits, so I wrote comments where you may want to adjust them.

var CRNP_Oban = ee.FeatureCollection("users/sijehasuk/CRNP_Oban2"),
    ndvi = ee.ImageCollection("MODIS/061/MOD13Q1");
    ndvi = ndvi.map(function(image){ return image.clip(CRNP_Oban)});

// Create function with filter of quality pixels
var mask = function(im) {
  return im.updateMask(im.select("SummaryQA").eq(0));
};

ndvi = ndvi.map(mask);
// Explore the variable to view QA
print(ndvi);

// This function sets the starting position for the bit and the number of
// positions necessary.
var GetQABits = function(image, bitStart, numBits){
    var bitmapped = image.select('DetailedQA').rightShift(bitStart).mod(Math.pow(2, numBits));
    return bitmapped;
};

var MaskToBestBits = function(image){
  var detailqa = image.select('DetailedQA');
  var VIQual = GetQABits(detailqa, 0, 2);
  var VIUse = GetQABits(detailqa, 2, 4);
  var VIaer = GetQABits(detailqa, 6, 2);
  var Viadjcl = GetQABits(detailqa, 8, 1);
  var atmbrdf = GetQABits(detailqa, 9, 1);
  var mxdcl = GetQABits(detailqa,  10, 1);
  var lnwt = GetQABits(detailqa, 11, 3);
  var snowice = GetQABits(detailqa, 14, 1);
  var shadow = GetQABits(detailqa, 15, 1);
  // update the image mask to select the indicator values you.
  // the values in the following updateMasks are GUESSES based on what you posted
  // with regard to the indicator. For example, VI Quality has 2 values, 0
  // or 1, that are good, so used less than 2 ("lt(2)") to get either.
  return image.addBands(detailqa)
    .updateMask(VIQual.lt(2)) //Suggested, but not sure what you want here
    // .updateMask(VIUse.lt(8)) //Suggested, but not sure what you want here
    // .updateMask(VIaer.lt(3)) //Suggested, but not sure what you want here
    // .updateMask(Viadjcl.eq(1)) //Suggested, but not sure what you want here
    // .updateMask(atmbrdf.lt(1)) //Suggested, but not sure what you want here
    // .updateMask(mxdcl.eq(0)) //Suggested, but not sure what you want here
    // .updateMask(lnwt.eq(1)) //Suggested, but not sure what you want here
    // .updateMask(snowice.eq(0)) //Suggested, but not sure what you want here
    // .updateMask(shadow.eq(0)); //Suggested, but not sure what you want here
};

var newndvi = ee.ImageCollection(ndvi)
.filterDate('2000-01-01','2020-01-01')
.map(MaskToBestBits)
.select("NDVI")
.filterBounds(CRNP_Oban)
.map(function(im) { return im.clip(CRNP_Oban)});

print('newndvi', newndvi);


Map.addLayer(newndvi, null, 'newndvi');
Map.addLayer(ndvi, null, 'ndvi')
Map.setCenter(8.53, 5.56, 9);
Map.addLayer(CRNP_Oban, null, 'CRNP Oban')