3

I have written a rather simple script for data selection and subsequent export in GEE:

//Create region of interest -> Should be replaced by manual selection of ROIs or ROI center points and subsequent processing of all ROIs by the process implemented for a single ROI in the following
var roi = ee.Geometry.Rectangle([10, 45, 11, 46]);
//Load Sentinel-2 image collection
var coll_s2 = ee.ImageCollection("COPERNICUS/S2");
//Filter for 10 most recent almost cloud-free images
var coll_s2_filtered = coll_s2.filterBounds(roi)
                      .filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 5))
                      .sort('system:time_start',false)
                      .limit(10);
//Create RGB composite
var s2_median = coll_s2_filtered.reduce(ee.Reducer.median()).select(['B4_median','B3_median','B2_median']);
//Export image
Export.image.toDrive({
  image: s2_median,
  description: 'Sentinel2',
  scale: 10,
  region: roi
});

What I actually want to do is to avoid hardcoding of a single ROI. Instead, I'd like to select an arbitrary number of rectangular ROIs around the globe and do the same export process for every ROI. I am thinking about doing this by selecting individual marker points and simply adding a buffer around their coordinates in order to achieve rectangular ROIs. However, I don't seem to be able to wrap my head around two critical things:

  1. How to do all this in a batch-processing manner (i.e. as a mapping over a feature collection or as an iteration through the elements of a list etc.)?
  2. How to extract the coordinates from the manually selected marker points in the first place?

I am kind of stuck here.

EDIT: I am now following an advice that suggested to make use of FeatureCollections, mapping, buffers and bounding boxes. I first tried what I want to do in a simple minimal example that actually worked:

var listOfMarkers = /* color: #98ff00 */ee.FeatureCollection(
    [ee.Feature(
        ee.Geometry.Point([6.8115234375, 60.88836817267309]),
        {
          "system:index": "0"
        }),
    ee.Feature(
        ee.Geometry.Point([15.1611328125, 57.469327688204295]),
        {
          "system:index": "1"
        }),
    ee.Feature(
        ee.Geometry.Point([27.0703125, 62.95584745563692]),
        {
          "system:index": "2"
        })]);

     // A function to create a bounding box out of a point
     var createBBox = function(featurePoint) {
       var rectangle = featurePoint.buffer(100000).bounds();
       return ee.Feature(rectangle);
     };

     var roi = listOfMarkers.map(createBBox);
     Map.addLayer(roi)
     print(roi)

     var coll_s2 = ee.ImageCollection("COPERNICUS/S2");
     //Filter for 10 most recent almost cloud-free images
     var coll_s2_filtered = coll_s2.filterBounds(roi)
                  .filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 5))
                  .sort('system:time_start',false)
                  .limit(10);

For the 3 arbitrarily chosen points in the feature collection, this script would give me the 10 most recent Sentinel-2 images with a cloud coverage below 5%. However, as said before, I want to get these 10 images per every marker-defined ROI. So I tried to extend the script:

//Enter same listOfMarkers as above here...
//A function to create an Image Collection out of a marker point
var createImageCollection = function(featurePoint) {
  var rectangle = ee.Feature(featurePoint.buffer(100000).bounds());
  var coll_s2 = ee.ImageCollection("COPERNICUS/S2");
     //Filter for 10 most recent almost cloud-free images
     var coll_s2_filtered = coll_s2.filterBounds(rectangle)
                  .filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 5))
                  .sort('system:time_start',false)
                  .limit(10);

     return coll_s2_filtered;
  };

  var s2data = listOfMarkers.map(createImageCollection);
  Map.addLayer(s2data)
  print(s2data)

Now, unfortunately, this second script gives me an error: "FeatureCollection (Error), Error in map(ID=2): Feature, argument 'geometry': Invalid type. Expected: Geometry. Actual: Feature."

This seems kind of strange, as I am also using ee.Feature for roi in the first example, and it worked nonetheless. Can anybody shed light on this, or explain how I can convert the rectangle created by

  var rectangle = ee.Feature(featurePoint.buffer(100000).bounds());

into an ee.Geometry.Rectangle object?

6

Here's an example that hopefully does something like what you want, but you'll need to modify it to meet your requirements for "manually selected marker points" and Sentinel 2.

In general, do NOT use for-loops or getInfo(). To be clear, DO NOT USE FOR LOOPS unless you have a very good reason to be doing so. If you don't know, use map() for all the reasons described here, here, here and here. (And check the same guides for why to NOT use getInfo() or convert to a list as I've done here.) The reason it's OK to do this here (necessary, in fact) is because Export is a client side function and you can't use a client function in map().

// Get some imagery to play with.
var landsat = ee.ImageCollection("LANDSAT/LC08/C01/T1")
    .filterDate('2016-01-01', '2017-01-01');

var composite = ee.Algorithms.Landsat.simpleComposite({
  collection: landsat,
  asFloat: true
});

var rgbVis = {bands: ["B4", "B3", "B2"], min:0, max: 0.3};
Map.addLayer(composite, rgbVis, "RGB");

// This is a hacky way to get a pixel grid at arbitrary resolution.
var pixels = ee.Image.random().multiply(10000000).toInt32()
    .reduceToVectors({
      reducer: ee.Reducer.countEvery(),
      geometry: Map.getBounds(true),
      geometryType: 'bb' ,
      eightConnected: false,
      scale: 20000,
      crs: 'EPSG:4326'
    });
Map.addLayer(pixels);

// Only do this is you have a few regions.  Not suitable for 
// large feature collections.
var pixelsList = pixels.toList(pixels.size());

// This is one of the few places in the EE API where you need 
// a for-loop and a getInfo() call.  Export is a client function.
for (var i=0; i<pixels.size().getInfo(); i++) {
  Export.image.toDrive({
    image: composite, 
    description: 'foo_' + i, 
    fileNamePrefix: 'foo_' + i, 
    region: ee.Feature(pixelsList.get(i)).geometry(), 
    scale: 30, 
  });
}

If you really need precise control over the export regions, you can make lists of coordinates and turn those into a collection of the ROIs you want to export. All that aside, you still need to click 'Run' on the exports. If you want it completely automated, use the Python API and ee.batch.Export followed by task.start().

  • Many thanks for your answer. Unfortunately, using Ubuntu 16.04 and Google Chrome 61.0.3163.100 (64 bit), I am not able to run this code example without Chrome crashing. Could you maybe explain what happens in the code section you described as a hacky way to get a pixel grid at arbitrary resolution? Thanks a lot! – Michael Sep 28 '17 at 9:07
  • 1
    What do you mean by "Chrome crashing"? I am sure it has nothing to do with the reduceToVectors and everything to do with the for loop and the getInfo(). If you are zoomed out too far, resulting in Map.getBounds(true) returning a large area, resulting in pixelsList being too large, it's either not going to work or it's going to take a long time. Be sure to check the value of pixelsList.size() before proceeding. If it's more tasks than you're willing to click, don't run it this way. Use the python API. – Nicholas Clinton Sep 28 '17 at 17:10
  • You're right, zooming in more let me run the process. – Michael Sep 29 '17 at 6:22
0

Meanwhile I have found a solution, which I'd like to share:

//FeatureCollection containing arbirtrary point markers, 
//which can also result from manual selection 
var listOfMarkers = /* color: #98ff00 */ee.FeatureCollection(
        [ee.Feature(
            ee.Geometry.Point([6.8115234375, 60.88836817267309]),
            {
              "system:index": "0"
            }),
        ee.Feature(
            ee.Geometry.Point([15.1611328125, 57.469327688204295]),
            {
              "system:index": "1"
            }),
        ee.Feature(
            ee.Geometry.Point([27.0703125, 62.95584745563692]),
            {
              "system:index": "2"
            })]);

//Function for the export of Sentinel-2 images corresponding to ROIs
//spread around the point markers. The size of the ROIs is determined 
//by the parameter passed to ee.Feature.buffer().
var exportROIimages = function(featurePoint,number) {
  var rectangle = ee.Feature(featurePoint.buffer(20000).bounds());
  var roi = rectangle.geometry();

  Map.addLayer(roi)

  //Load 10 most recent (almost) cloud-free images corresponding to the ROI 
  var coll_s2 = ee.ImageCollection("COPERNICUS/S2");
  var coll_s2_filtered = coll_s2.filterBounds(roi)
              .filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 5))
              .sort('system:time_start',false)
              .limit(10);

  //Create RGB composite. I use the median of the 10 most recent images
  //to enhance the changes that really no clouds are present and the 
  //complete ROI is covered.
  var s2_median = coll_s2_filtered.reduce(ee.Reducer.median())
                   .select(['B4_median','B3_median','B2_median']);

 //Export image with 10m pixel spacing                        
 Export.image.toDrive({
 image: s2_median,
 description: 'image_' + number,
 scale: 10,
 region: roi
 });

 return 1;
};


//Mapping the function over the marker point FeatureCollection is 
//what I'd like to do, but it doesn't seem to work as Export.image.toDrive
//is a client-based function, which cannot be mapped.
//listOfMarkers.map(exportROIimages);

//Workaround: conversion to list and for-loop
//(I am intending to apply this to at most 100 ROI markers)
var listOfMarkersList = listOfMarkers.toList(100);
var numberOfMarkers = listOfMarkersList.size().getInfo();

for (var i = 0; i < numberOfMarkers; i += 1) {
exportROIimages(ee.Feature(listOfMarkersList.get(i)),i);  
}

Maybe there is a more elegant solution to parts of this code - but it does what it should do.

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.