1

I need to filter out all images of a ee.imageCollection that is not complete (does not cover the entire geometry). I tried using .filterMetadata('MGRS_TILE') and I didn't make it. Any idea?

enter image description here

Here is my script: Google Earth Engine Script

var perimeter = ee.FeatureCollection(table);

var start = '2019-03-16'
var end = '2019-03-25'

var tiles = ['T20JML', 'T20JML']
var dataset = ee.ImageCollection('COPERNICUS/S2_SR').filterMetadata('MGRS_TILE', 'equals', '20JML')
                  .filterBounds(perimeter)
                  .filterDate(start, end)
                  .filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 10);
print(dataset)
var ultima = dataset.first()

var imagen = ee.Image(ultima).normalizedDifference(['B8', 'B4'])

var minMax = imagen.reduceRegion({reducer: ee.Reducer.minMax(), 
                               geometry: perimeter})

var minMax = minMax.rename(minMax.keys(), ['max','min']);  

minMax.evaluate(function(val){
  var min = val.min;
  var max = val.max;

var visParam = {
        min: min,
        max: max,
        palette: ['FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
    '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
    '012E01', '011D01', '011301']
        };
        
Map.addLayer(imagen.clip(perimeter), visParam, "Imagen Ultimo NDVI");
Map.setOptions('SATELLITE')
Map.centerObject(perimeter, 16)
Map.addLayer(perimeter, {}, "Perimetro Lote");

  
})
1
  • I don't think you can remove these images from the image collection as your perimeter is actually contained within the footprint of the scene. So from the collection filtering point-of-view, all is good, even if you would use an ee.Filter.isContained() filter. But you do have these masked pixels close by the boundary. See here: code.earthengine.google.com/2b73c1a1ec0ef3784ffdf575fe113e62 Commented Nov 30, 2022 at 14:16

2 Answers 2

1

To filter out images with incomplete coverage of a geometry, you can calculate the percentage of the geometry covered by unmasked pixels and use that to filter the collection.

Calculating Pixel Coverage

First, you'll need a function that takes an image and a geometry and calculates the coverage. This is done by comparing the area of the geometry with the cumulative area of unmasked pixels within the geometry. The output will be an ee.Number between 0-100.

function get_percent_coverage(image, region) {
  var total_area = region.area();
  // Use the mask from the first band
  var mask = image.select(0).mask();
  
  // Calculate the total area of unmasked pixels in the region
  var covered_area = ee.Image.pixelArea().updateMask(mask).reduceRegion({
    reducer: ee.Reducer.sum(),
    geometry: region,
    scale: 10,
    bestEffort: true,
  }).getNumber("area");

  // What % of the region is covered by pixels? 
  return covered_area.divide(total_area).multiply(100);
}

Setting Metadata

Now we can map this function over the filtered collection and set it as a property on each image. Note that I had to expand your date range to get a complete image.

var dataset = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(perimeter)
  .filterDate("2019-03", "2019-04")
  .filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 10)
  .map(function(img) {return img.set("pct_coverage", get_percent_coverage(img, table))});

To confirm it worked, we can print the percent coverage from all of our images:

print(dataset.aggregate_array("pct_coverage"));

/*
0: 99.9094245329352
1: 84.60977544861765
2: 46.72417019416048
3: 46.72417019416048
*/

Notice that the first image covers 99.9%. The pixel area will probably never exactly match the region area because of difference in how raster and vector areas are calculated, so we'll want to take that into account when we filter the images.

Filtering Incomplete Images

Finally, we can filter the collection based on the pct_coverage property we set to get the first complete image.

var img = dataset
  .filter(ee.Filter.gt("pct_coverage", 99))
  .first();

Here's a simplified example script with the whole workflow: https://code.earthengine.google.com/1e4bc1172e2c68936b53cf4f8a8c7a27

Example image showing region fully covered by NDVI data

0

There are no other images with the characteristics you need (date range and cloud cover).

An option is to extend the date range to get the first image of the month, which covers your geometry.

var perimeter = ee.FeatureCollection(table);
var start = '2019-03-01'
var end = '2019-03-31'

var tiles = ['T20JML', 'T20JML']
var dataset = ee.ImageCollection('COPERNICUS/S2_SR')
                  .filterBounds(perimeter)
                  .filterDate(start, end)
                  .filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 10)

var ultima = dataset.first()

var imagen = ee.Image(ultima).normalizedDifference(['B8', 'B4'])

var minMax = imagen.reduceRegion({reducer: ee.Reducer.minMax(), 
                               geometry: perimeter})

var minMax = minMax.rename(minMax.keys(), ['max','min']);  

minMax.evaluate(function(val){
  var min = val.min;
  var max = val.max;

var visParam = {
        min: min,
        max: max,
        palette: ['FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
    '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
    '012E01', '011D01', '011301']
        };
        
Map.addLayer(imagen.clip(perimeter), visParam, "Imagen Ultimo NDVI");
Map.setOptions('SATELLITE')
Map.centerObject(perimeter, 16)
Map.addLayer(perimeter, {}, "Perimetro Lote");


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.