Skip to main content
4 of 4
more descriptive/precise title
JepsonNomad
  • 2.2k
  • 1
  • 14
  • 35

Earth Engine ee.ImageCollection export mis-projected

I am exporting MOD10A1 imagery from Google Earth Engine and the exported data are not aligned properly in space. I tried defining the export crs as per Nicholas Clinton's advice here but to no avail. I discovered the issue after exporting both the raster and vector data and projecting simultaneously in R. They do not align (note that the vector ROI drops off the lower-left side of the plotting window):

VectorRasterMismatch

I verified that it is not a problem with the vector data by opening it separately in Google Earth Pro - the vector data are projected fine. And I believe the actual data in the raster are correct; low-quality pixels are unmasked with the value set to 255, and we can clearly see the outline of Mono Lake, which is the focus of this toy ROI. Thus, I am led to believe that the issue is in the proj information for the raster dataset.

Reproducible example (first the GEE data acquisition and export):

//// ROI
var Sample = ee.Geometry.Polygon([
  [[-119.22, 38.06], [-119.23, 37.97], [-119.01, 37.91], 
  [-118.89, 37.93], [-118.85, 38.02], [-119.01, 38.09],
  [-119.22, 38.06]]
]);

// Generate Region of Interest
var ROI = ee.FeatureCollection(Sample);

// visualize
Map.centerObject(ROI, 7); // Center on the Grand Canyon.
Map.addLayer(ROI, {color: '6a0dad', opacity:0.99}, 'ROI');

//// Data filters
// Create a QA mask + clipping function
var masker = function(image){ 
  var mask = image.select('NDSI_Snow_Cover_Basic_QA').lte(1);
  var maskedImage = image.updateMask(mask);
  return maskedImage.unmask(255).clip(ROI);
};

//// Acquire data
// Compile the data
var dataset = ee.ImageCollection('MODIS/006/MOD10A1')
                  .filter(ee.Filter.date('2019-01-01', '2020-01-01'))
                  .map(masker);
print("MOD10A1 Image Collection", dataset);

// Select the data you're interested in
var ndsi = dataset.select('NDSI_Snow_Cover');
print("MOD10A1 NDSI Collection",ndsi);

// Plot the data
Map.addLayer(ndsi, {}, 'NDSI');


//// PREPARE DATA FOR EXPORT
// Thanks Tyler Erickson for the stacking function
// https://gis.stackexchange.com/a/254778/67264
// The code below is slightly modified from Erickson's
// approach in order to rename using dateString in
// stackCollection function
var stackCollection = function(collection) {
  // Create an initial image.
  var first = ee.Image(collection.first()).select([]);

  // Write a function that appends a band to an image.
  var appendBands = function(image, previous) {
      var dateString = ee.Date(image.get('system:time_start')).format('yyyy-MM-dd');
      return ee.Image(previous).addBands(image.rename(dateString));
  };
  return ee.Image(collection.iterate(appendBands, first));
};
var ndsi_img = stackCollection(ndsi);
print("NDSI stacked collection", ndsi_img);


//// Export the data
// Export table for plotting
Export.table.toDrive({
  collection: ROI,
  description: 'SampleROI',
  folder: "AlignmentTroubleshooting",
  fileFormat: 'SHP'
});

// Export imagery
// Specify a crs as per Nicholas Clinton's advice
// https://gis.stackexchange.com/a/257647/67264
// Export a cloud-optimized GeoTIFF.
// See https://developers.google.com/earth-engine/exporting
Export.image.toDrive({
  image: ndsi_img,
  description: 'NDSI_2019',
  folder: "AlignmentTroubleshooting",
  scale: 500,
  region: ROI,
  crs: ndsi_img.select(0).projection(),
  fileFormat: 'GeoTIFF',
  formatOptions: {
    cloudOptimized: true
  }
});

Reproducible part II (R import and plotting):

library(raster)
library(rgdal)

# Raster
ras = raster::stack("NDSI_2019.tif")
proj4string(ras)

# ROI
roi = readOGR(".",
              "SampleROI")
proj4string(roi)
roi = spTransform(roi, CRSobj = crs(ras))

# Plot
plot(ras[[150]])
plot(roi, add = T)

The NDSI_2019.tif and SampleROI.shp files can be accessed without running the Earth Engine code here.

JepsonNomad
  • 2.2k
  • 1
  • 14
  • 35