1

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

Kindly help to resolve.

0

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.

5
  • Thanks for the clarification. Much appreciated.
    – Vineeth
    Aug 2 '21 at 14:29
  • I've never worked with bitmask in GEE so I'm finding it hard to grasp. Basically what I understood is that a value "0" in "Bits 2-3" represent "Good data quality". In your comment, you mentioned that "only a check for '1' in bit 2 is necessary". I didn't understand that. Could you please explain. Also I have another doubt. If I wanted to use bitmask 1, what should be written in place of "var bitMask2" and "var bitMask3" in my GEE code. Thanks in advance.
    – Vineeth
    Aug 2 '21 at 14:35
  • See this answer I gave a couple of days ago clarifying bit masks. gis.stackexchange.com/questions/405056/… Aug 2 '21 at 14:46
  • Ok. I understood the concept of 1<<2 and 1<<3. I'm still having doubt regarding ".eq()". Here in my case, actually what I did is that, since I required "0: Good data quality", I gave the command as ".eq(0)". So if I required "1: Other quality data" should I give the command as ".eq(1)" or is there a different logic for that.
    – Vineeth
    Aug 2 '21 at 15:13
  • You are correct, If you were looking for the 1, then you would put eq(1). some satellites have a QCMask that tells you what type of cloud it is. putting the bit in different places. For example bit 5 might be cloud. but bit 7 might mean cirrus, which perhaps you are OK with. So you don't want to mask if there is cloud, only if there is something other than cirrus. So you might check for bit 6 which could be other clouds. so if bit 6 eq(0) then you are OK. Even if bit 5 is 1. Aug 3 '21 at 16:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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