Search code examples
google-earth-engine

extract the values for all pixels in a polygon in google earth engine


I defined a polygon

var polygon = ee.Geometry.Polygon([114, 0.37, 114, 2.04, 112, 2.04, 112, 0.37]);

and a dataset that needs to be processed for the above polygon

var dataset = ee.ImageCollection('NASA/NEX-GDDP');

for a selected date

var startDate = ee.Date('1980-01-01');
var endDate = ee.Date('1980-01-02');

The dataset has 3 bands pr, tasmax and tasmin and I am selecting the one that I need to process

var dataset = ee.ImageCollection('NASA/NEX-GDDP')
             .filter(ee.Filter.date(startDate,endDate))
             .filter(ee.Filter.bounds(polygon))
             .select('tasmax');


Map.addLayer(dataset)

I want to export the data for all the grids falling under the polygon along with their respective lat long. Since there are 21 features (GCMs) for a single day, I am expecting the final data to have number rows equal to number of grids in polygon X 21 features (GCMs)

var dailyImg = dataset.toBands();

Export.table.toDrive({
    collection: dailyImg,
    description: 'hist_tx',
    fileFormat: 'CSV',
});

When I try to do this, I get an error

Error: Invalid argument: 'collection' must be a FeatureCollection.

How can I solve this? In addition, even after restricting my spatial area to the polygon, the map still displays the data for the entire globe? Why is this happening?


Solution

  • Error: Invalid argument: 'collection' must be a FeatureCollection.

    Export.table is for exporting tables, also known as FeatureCollections. You have an image, not a table.

    The most efficient way to get the data out of Earth Engine is to use Export.image instead, then convert the downloaded GeoTIFF to suit your R program. However, since this dataset is very small, downloading it as a CSV will work fine, and the tool for that is ee.Image.sample which converts a region of an Image to a FeatureCollection.

    var collection = dailyImg.sample({
      region: polygon,
      geometries: true,  // This specifies that you want the lat-long, rather
                         // than image samples without any position information.
    });
    

    If you export this, you will get the position in a single column in GeoJSON format. That's probably not what you want, so we can convert it to columns:

    var collection_with_latlon = collection.map(function (feature) {
      var coordinates = feature.geometry().transform('epsg:4326').coordinates();
      return feature.set('lon', coordinates.get(0), 'lat', coordinates.get(1));
    });
    

    Here's everything put together as a working example:

    var polygon = ee.Geometry.Polygon([114, 0.37, 114, 2.04, 112, 2.04, 112, 0.37]);
    var startDate = ee.Date('1980-01-01');
    var endDate = ee.Date('1980-01-02');
    var dataset = ee.ImageCollection('NASA/NEX-GDDP')
                 .filter(ee.Filter.date(startDate,endDate))
                 .filter(ee.Filter.bounds(polygon))
                 .select('tasmax');
    
    Map.addLayer(polygon);
    
    var dailyImg = dataset.toBands();
    var collection = dailyImg.sample({
      region: polygon,
      geometries: true,  // This specifies that you want the lat-long.
    });
    
    // Break point coordinates up into properties (table columns) explicitly.
    var collection_with_latlon = collection.map(function (feature) {
      var coordinates = feature.geometry().transform('epsg:4326').coordinates();
      return feature.set('lon', coordinates.get(0), 'lat', coordinates.get(1));
    });
    
    print(collection_with_latlon);
    
    Export.table.toDrive({
        collection: collection_with_latlon,
        description: 'hist_tx',
        fileFormat: 'CSV',
    });
    

    In addition, even after restricting my spatial area to the polygon, the map still displays the data for the entire globe? Why is this happening?

    Filtering a collection to geometry only omits images that do not intersect the geometry. In this case, the images cover the entire globe, so no images are filtered out. In order to clip the images to the collection you would have to specify that, e.g.:

    var dailyImg = dataset.toBands().clip(polygon);
    

    However, this is not necessary if you are using .sample(), because that operation has its own region parameter and will not use any pixels outside the polygon.