4

I am trying to get cloud masking to work properly with the 'COPERNICUS/S2_SR' surface reflectance dataset.

I can get it to somewhat work with my code below using the TOA dataset 'COPERNICUS/S2'.

Additionally, there seems to be duplicate dates which I had with this code using LandSat 8, that was solved by locking rows and paths. From my research I think I need to lock it to granules with Sentinel Data which I believe the area to be 'RN'

What is the correct way to cloud mask Sentinel 2 Surface Reflectance for my areas of interest and remove duplicate values?

var ROI = ee.Geometry.Point([141.041807, -34.033391]);

//Center the Map
Map.setCenter(141.041807, -34.033391, 15);

var kulkurna_A = ee.Geometry.Polygon([
  [
    [141.045513, -34.031637], [141.045252, -34.031597], [141.045002, -34.031647], 
    [141.044686, -34.031915], [141.044520, -34.032018], [141.040777, -34.035606], 
    [141.040349, -34.036386], [141.041069, -34.036233], [141.041372, -34.035775], 
    [141.042721, -34.034827], [141.043463, -34.034567], [141.045337, -34.031958]
    ]
    ]);

Map.addLayer(kulkurna_A, {color: 'blue'}, 'Kulkurna A');


var kulkurna_B = ee.Geometry.Polygon([
  [
    [141.041761, -34.031699], [141.041590, -34.031794], [141.039482, -34.033245], 
    [141.038768, -34.034018], [141.039511, -34.035584], [141.039796, -34.034948], 
    [141.039535, -34.033698], [141.040783, -34.032723], [141.041335, -34.032393], 
    [141.041793, -34.031935]
    ]
    ]);

Map.addLayer(kulkurna_B, {color: 'red'}, 'Kulkurna B');



var ens = [
ee.Feature(kulkurna_A, {label : 'Kulkurna A'}),
ee.Feature(kulkurna_B, {label : 'Kulkurna B'})
];

// Create image collection of S-2 imagery for the perdiod 2016-2018
var S2 = ee.ImageCollection('COPERNICUS/S2')

      //filter start and end date
      .filter(ee.Filter.calendarRange(2013,2019,'year'))
      .filter(ee.Filter.calendarRange(1,12,'month'))

      //filter according to drawn boundary
      .filterBounds(ROI);

// Function to mask cloud from built-in quality band
// information on cloud
var maskcloud1 = function(image) {
var QA60 = image.select(['QA60']);
return image.updateMask(QA60.lt(1));
};

// Function to calculate and add an NDVI band
var addNDVI = function(image) {
return image.addBands(image.normalizedDifference(['B8', 'B4']));
};

// Add NDVI band to image collection
var S2 = S2.map(addNDVI);

// Extract NDVI band and create NDVI median composite image
var NDVI = S2.select(['nd']);
var NDVI = NDVI.median();


//------------------------------------------------
//------------------------------------------------
//Start graphing results
//------------------------------------------------

// Create an empty panel in which to arrange widgets.
// The layout is vertical flow by default.
var panel = ui.Panel({style: {width: '400px'}})
    .add(ui.Label('NDVI Charts - Sentinel 2'));


//Graph all regions on same chart
var all_regions_graph = ui.Chart.image.seriesByRegion({
  imageCollection: S2, 
  regions: ens, 
  band: 'nd',
  reducer: ee.Reducer.mean(),
  scale: 30,
  seriesProperty: 'label'
      })
      .setChartType('ScatterChart')
      .setOptions({
  title: 'Kulkurna A & B',
  trendlines: {0: {color: 'purple'}},
  hAxis: {title: 'Time'},
  vAxis: {title: 'NDVI'},
  lineWidth: 2,
  pointSize: 3,
  });
panel.widgets().set(2, all_regions_graph);

//Graph individual region on its own chart
var kulkurna_A_graph = ui.Chart.image.seriesByRegion({
  imageCollection: S2, 
  regions: kulkurna_A, 
  band: 'nd',
  reducer: ee.Reducer.mean(),
  scale: 30,
  seriesProperty: 'kulkurna_A',
 })
      .setChartType('ScatterChart')
      .setOptions({
  title: 'Kulkurna A',
  trendlines: {0: {color: 'purple'}},
  hAxis: {title: 'Time'},
  vAxis: {title: 'NDVI'},
  lineWidth: 2,
  pointSize: 3,
  series: {0: {color: 'blue'}},
  });
panel.widgets().set(3, kulkurna_A_graph);

//Graph individual region on its own chart
var kulkurna_B_graph = ui.Chart.image.seriesByRegion({
        imageCollection: S2, 
        regions: kulkurna_B,
        band: 'nd',
        reducer: ee.Reducer.mean(),
        scale: 30,
        seriesProperty: 'Kulkurna_B'
 })
      .setChartType('ScatterChart')
      .setOptions({
  title: 'Kulkurna B',
  trendlines: {0: {color: 'purple'}},
  hAxis: {title: 'Time'},
  vAxis: {title: 'NDVI'},
  lineWidth: 2,
  pointSize: 3,
  series: {0: {color: 'red'}},
  });
panel.widgets().set(4, kulkurna_B_graph);


// Add the panel to the ui.root.
ui.root.add(panel);
1

Sentinel 2 Surface Reflectance (SR) dataset comes with 2 ways for removing clouds (and the rest of the "bad bits" like cloud shadows, dark pixels, etc). As well as Sentinel 2 TOA dataset it comes with QA60 bit band, but also comes with the "Scene Classification Map" band (SCM) which is not bit encoded but just a classified data (see SCL Class Table here)

Here is a way of applying it:

var cld = require('users/fitoprincipe/geetools:cloud_masks')

var s2SR = ee.ImageCollection('COPERNICUS/S2_SR')
              //filter start and end date
             .filter(ee.Filter.calendarRange(2013,2019,'year'))
             .filter(ee.Filter.calendarRange(1,12,'month'))
             //filter according to drawn boundary
             .filterBounds(ROI)
             .filterMetadata('CLOUD_COVERAGE_ASSESSMENT', 'greater_than', 50)

var test_image = s2SR.first()

Map.addLayer(test_image, {bands:['B8', 'B11', 'B4'], min:0, max:5000}, 'test image')

var masked = cld.sclMask(['cloud_low', 'cloud_medium', 'cloud_high', 'shadow'])(test_image)
Map.addLayer(masked, {bands:['B8', 'B11', 'B4'], min:0, max:5000}, 'masked')

Argument options for cld.sclMask are: 'cloud_low', 'cloud_medium', 'cloud_high', 'shadow', 'saturated', 'dark', 'cirrus', 'snow' and 'water' (you can also mask out 'vegetation' and 'bare_soil', but take in count that your are masking this out)

You can look at the source code of geetools:cloud_masks here

  • I have tried to use your suggestion but I cannot get it to work. Could you please show how your way with the geetools:cloudmask would work correctly with the initial code posted. – Damien N Sep 9 at 1:13
  • Ok, first, I don't want to be rude, but there is a lot of extra code in your question that is not related to it; you should only post code related to the question. Second, you have to load SR collection: COPERNICUS/S2_SR, see: code.earthengine.google.com/36047f069484449f3dfb9e563c12aeb5. Last: I've done a lot of knee boarding over the Murray River, graet place! =) – Rodrigo E. Principe Sep 9 at 11:04
  • How did it go @DamienN? – Rodrigo E. Principe Sep 10 at 1:41
  • Hi Rodrigo, not sure how to respond, I tried for a few hours to make it work with my code. I did load the COPERNICUS/S2_SR collection when trying your example. I also noticed in your example that there are clouds presented in the display (Not sure why). I am new to coding in GEE and JavaScript so presenting my actual use case / code is the best way I feel I can convey my question and learn how to generate the code to meet the goal. Also, I do appreciate the time you have taken to answer my question. – Damien N Sep 10 at 7:28
  • Here is your exact code: code.earthengine.google.com/f4441a2f314927c30cb31dd9b7e3c196 with the modifications needed.. I think the problem was that you were not mapping the cloud masking function (see line 61 in that code) – Rodrigo E. Principe Sep 10 at 11:46
2

For Sentinel 2 the QA60 band contains info on whether the pixel is cloudy or not in 10th and 11th bit for opaque and cirrus clouds. So we can check that by checking values that have 1 on 10th and 11th bit or we can use bitwiseAnd to achieve the same.

// Function to mask cloud from built-in quality band
// information on cloud
var maskcloud1 = function(image) {
  var QA60 = image.select(['QA60']);
  var clouds = QA60.bitwiseAnd(1<<10).or(QA60.bitwiseAnd(1<<11);// this gives us cloudy pixels
  return image.updateMask(clouds.not()); // remove the clouds from image
};

  • I Have tried to incorporate the maskcloud1 function and although it appears to be removing clouds, I am two readings on each day. Could you please post a complete method using my code from above, as I feel like I am missing something else, like Map.addLayer() etc.. – Damien N Sep 9 at 1:04

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.