google-earth-engine – Using MOD11A1 Bitmask to Select ‘Good Data Quality’ Pixels for LST Time Series in Google Earth Engine

google-earth-enginegoogle-earth-engine-javascript-apimaskingmodistime series

I'm trying to generate a daily LST time series over a region by selecting only the good quality pixels. The code I've written is attached below along with the dataset description. My aim is to select pixels that have "0: Good data quality" in "Bits 2-3: Data quality flag" under QC_Day and QC_Night bands. But I'm not sure whether the code actually filters the pixels based on my requirement.

My GEE code:

var Bhagirathi = ee.FeatureCollection("users/Vineeth_Russell/NRSC_Bhagirathi_shp");

//Terra and Aqua Day time LST Daily Global 1km
var day_terra = ee.ImageCollection("MODIS/006/MOD11A1")
                  .filter(ee.Filter.date('2000-03-05', '2010-01-01'))
                  .select('LST_Day_1km','QC_Day');


//Terra and Aqua Night time LST Daily Global 1km
var night_terra = ee.ImageCollection("MODIS/006/MOD11A1")
                    .filter(ee.Filter.date('2000-03-05', '2010-01-01'))
                    .select('LST_Night_1km','QC_Night');


//create mask to extract only highest quality day band data 
var filterDay = function(image){ 
  var qa = image.select('QC_Day');
  var bitMask2 = 1 << 2;
  var bitMask3 = 1 << 3;
  var mask = qa.bitwiseAnd(bitMask2).eq(0).and(qa.bitwiseAnd(bitMask3).eq(0));
  return image.updateMask(mask);
};


//create mask to extract only highest quality night band data 
var filterNight = function(image){ 
  var qa = image.select('QC_Night');
  var bitMask2 = 1 << 2;
  var bitMask3 = 1 << 3;
  var mask = qa.bitwiseAnd(bitMask2).eq(0).and(qa.bitwiseAnd(bitMask3).eq(0));
  return image.updateMask(mask);
};


//apply mask to image collection
var lst_d_terra = day_terra.map(filterDay);
var lst_n_terra = night_terra.map(filterNight);


//Scale to Kelvin and convert to Celsius, set image acquisition time (Terra Day-time)
lst_d_terra = lst_d_terra.map(function(img) {
  return img.select('LST_Day_1km')
    .multiply(0.02)
    .subtract(273.15)
    .copyProperties(img, ['system:time_start','system:time_end']);
});


//Scale to Kelvin and convert to Celsius, set image acquisition time (Terra Night-time)
lst_n_terra = lst_n_terra.map(function(img) {
  return img.select('LST_Night_1km')
    .multiply(0.02)
    .subtract(273.15)
    .copyProperties(img, ['system:time_start','system:time_end']);
});


//Charting Terra Day-time LST over Bhagirathi
print(Chart.image.series(lst_d_terra, Bhagirathi,ee.Reducer.mean(),1000).setOptions({
   title: "Time Series of MODIS Terra Daily Day-time LST across Bhagirathi",
   hAxis: {
     title: "Time Period",
     titleTextStyle: {italic: false, bold: true}
   },
   vAxis: {
     title: "LST",
     titleTextStyle: {italic: false, bold: true}
   },
   colors: ["Red"]
}));


//Charting Terra Night-time LST over Bhagirathi
print(Chart.image.series(lst_n_terra, Bhagirathi,ee.Reducer.mean(),1000).setOptions({
   title: "Time Series of MODIS Terra Daily Night-time LST across Bhagirathi",
   hAxis: {
     title: "Time Period",
     titleTextStyle: {italic: false, bold: true}
   },
   vAxis: {
     title: "LST",
     titleTextStyle: {italic: false, bold: true}
   },
   colors: ["Blue"]
}));

Dataset description:
https://developers.google.com/earth-engine/datasets/catalog/MODIS_006_MOD11A1?hl=en

How do I resolve this?

Best Answer

Your code seems to be doing what it should, however you are adding unnecessary bits. Your code

var filterNight = function(image){ 
  var qa = image.select('QC_Night');
  var bitMask2 = 1 << 2;
  var bitMask3 = 1 << 3;
  var mask = qa.bitwiseAnd(bitMask2).eq(0).and(qa.bitwiseAnd(bitMask3).eq(0));
  return image.updateMask(mask);
};

If you look at the QC bitmask,

Bits 2-3: Data quality flag
0: Good data quality
1: Other quality data
2: TBD
3: TBD

all you really need is to check for a "1" in bit 2. a value of 2, or 3 has been reserved for future use, but at the moment is in the To Be Determined state. So you can ignore them.

The line for bitmask3, and its corresponding bitwiseAnd() is not necessary. However, it should not affect your result, a mask would have been applied if ever the data was not good data quality.

The result that you are seeing in your charts should be correct.

In future, include your code in your question, so that it is easier for future people to search.

Related Question