1

Using this code I am trying to extract surface area of water bodies but some ROIs are not completely covered because of missing adjacent tile (see image). What can I do to add and mosaic the adjacent Sentinel 1 tile?

code link: https://code.earthengine.google.com/24e31dff3a145f38bbfd7deb59636da5

// Load region defined by polygon and add it to the map

Map.addLayer(roi, {}, 'ROI')
Map.centerObject(roi, 12)
          

//Load Sentinel-1 SAR collection and filter according to data collection type
var S1 = ee.ImageCollection('COPERNICUS/S1_GRD')
  .filterBounds(roi)
  .filterDate('2014-01-01','2020-07-29')
  .filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV'))

//Add first image to map to get an idea of what a SAR image looks like  
Map.addLayer(S1.first(),{bands: 'VV',min: -18, max: 0}, 'SAR image')
  
// Filter speckle noise
var filterSpeckles = function(img) {
  var vv = img.select('VV') //select the VV polarization band
  var vv_smoothed = vv.focal_median(100,'circle','meters').rename('VV_Filtered') //Apply a focal median filter
  return img.addBands(vv_smoothed) // Add filtered VV band to original image
}

// Map speckle noise filter across collection. Result is same collection, with smoothed VV band added to each image
S1 = S1.map(filterSpeckles)

//Add speckle filtered image to map to sompare with raw SAR image
Map.addLayer(S1.first(),{bands: 'VV_Filtered',min: -18, max: 0}, 'Filtered SAR image')

//Classify water pixels using a set threshhold 
//Here we are using -16. This is only an approximation and will result in some errors. Try adjusting the 
var classifyWater = function(img) {
  var vv = img.select('VV_Filtered')
  var water = vv.lt(-16).rename('Water')  //Identify all pixels below threshold and set them equal to 1. All other pixels set to 0
  water = water.updateMask(water) //Remove all pixels equal to 0
  return img.addBands(water)  //Return image with added classified water band
}

//Map classification across sentinel-1 collection and print to console to inspect
S1 = S1.map(classifyWater)
print(S1)

//Make time series of water pixels within region
var ClassChart = ui.Chart.image.series({
  imageCollection: S1.select('Water'),
  region: roi,
  reducer: ee.Reducer.sum(),
  scale: 100,
})
  .setOptions({
      title: 'Inundated Pixels',
      hAxis: {'title': 'Date'},
      vAxis: {'title': 'Number of Inundated Pixels'},
      lineWidth: 2
    })

//Set the postion of the chart and add it to the map    
ClassChart.style().set({
    position: 'bottom-right',
    width: '500px',
    height: '300px'
  });
  
Map.add(ClassChart)

// Create a label on the map.
var label = ui.Label('Click a point on the chart to show the image for that date.');
Map.add(label);

//Create callbakc function that adds image to the map coresponding with clicked data point on chart
ClassChart.onClick(function(xValue, yValue, seriesName) {
    if (!xValue) return;  // Selection was cleared.
  
    // Show the image for the clicked date.
    var equalDate = ee.Filter.equals('system:time_start', xValue);
    //Find image coresponding with clicked data and clip water classification to roi 
    var classification = ee.Image(S1.filter(equalDate).first()).clip(roi).select('Water'); 
    var SARimage = ee.Image(S1.filter(equalDate).first());
    //Make map layer based on SAR image, reset the map layers, and add this new layer
    var S1Layer = ui.Map.Layer(SARimage, {
      bands: ['VV'],
      max: 0,
      min: -20
    });
    Map.layers().reset([S1Layer]);
    var visParams = {
      min: 0,
      max: 1,
      palette: ['#FFFFFF','#0000FF']
    }
    //Add water classification on top of SAR image
    Map.addLayer(classification,visParams,'Water')
    
    // Show a label with the date on the map.
    label.setValue((new Date(xValue)).toUTCString());
  });

enter image description here

enter image description here

2 Answers 2

2

If I'm understanding your question correctly, the trouble is that you have images that are from the same day (seconds apart) that your code is looking at separately, but you want to look at them together as if they were one image.

If that's the case, you can use a function like this:

function makeMosaics(image) {
  var thisImage = ee.Image(image);
  var date = ee.Date(thisImage.get('system:time_start'));
  return ee.Image(S1.filterDate(date, date.advance(1,'day'))
                    .mosaic().copyProperties(image, ["system:time_start"]));
}

If you map this over your S1 ImageCollection, you'll get an ImageCollection containing mosaics of images from any day where data is available, merging the two images into one. There will be duplicates in the set, but this shouldn't be a problem for your chart.

2
  • sorry for the late reply, this worked perfectly. just one question the graph that I am getting for classified water, will it have average value for the area that is overlapping or will the value be added twice? Aug 10, 2020 at 7:13
  • Those pixels will only be counted once, but it's not an average - the mosaic() function picks whichever function is last in the ImageCollection for each pixel. Generally that's the most recent image, but if it matters for your analysis you should double check, because I'm not sure if that's how it works for sentinel.
    – Jean
    Aug 11, 2020 at 15:35
1

I just ran into this problem too, so I'm adding an improved version of Jean's solution where I filtered out duplicates using the ee.Filter.contains() function. Since the mosaic() function (and other collection reductions such as median(), mean(), etc.) does not store the geometry of the resulting image, I created a function to generate the merged geometry of all input images into a mosaic, which was added as the geometry of the resulting image.

function makeMosaics(image) {
  var thisImage = ee.Image(image);
  var date = ee.Date(thisImage.get('system:time_start'));
  // add only hour to exclude images from other paths
  var filteredDataset = S1Collection.filterDate(date, date.advance(1,'hour'));
  // add all image properties to the new image
  var toReturn = ee.Image(filteredDataset.mosaic()
                    .copyProperties(image,image.propertyNames())
                    );
  // add geometries
  var geometries = filteredDataset.map(function(img){
    return ee.Feature(img.geometry());
  });
  
  var mergedGeometries = geometries.union();
  return toReturn.set('system:footprint', mergedGeometries.geometry());
}

var mosaiced = S1Collection.map(makeMosaics);

// the final collection without duplicates
var post_filtered = mosaiced.filter(ee.Filter.contains('.geo', geometry));

Here is the working GEE code with Sentinel-1 example: https://code.earthengine.google.com/bea094399e67f5d5e4105d7b39051173

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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