6

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.

4
  • If you add the image to the GEE map, does it render in the correct location compared to the basemap?
    – Jon
    May 26, 2020 at 17:34
  • @Jon Yes it does. May 26, 2020 at 17:52
  • Could you upload the tif and SampleROI to try out the R part ? May 27, 2020 at 8:09
  • @CésarArquero done. May 27, 2020 at 15:58

1 Answer 1

3
+100

The issue is in the projection you are assigning the image for the export in Earth Engine. I could not get it to work with that projection either, but if I changed it to another they align as expect. You should export with a different projection, and then reproject layer if required.

//// Export the data
// Export table for plotting
Export.table.toDrive({
  collection: ROI,
  description: 'SampleROI',
  folder: "test",
  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
var UTM11n = 'EPSG:32611';
Export.image.toDrive({
  image: ndsi_img,
  description: 'NDSI_2019_UTM',
  folder: "test",
  crs: UTM11n,
  maxPixels: 1.0E13,
  formatOptions: {
    cloudOptimized: true
  }
});

enter image description here

The only answer I can give you is that I have had issues with GEE not recognizing uncommonly used projections in the past, even when they had EPSG codes, and have to use the workflow described. Maybe it has something to do with it not recognizing the

"semi_major" and "semi_minor" arguments to Sinusoidal projections.

on export. https://spatialreference.org/ref/sr-org/modis-sinusoidal-3/

1
  • 1
    Seems weird that they'd be able to handle transformations internally, but then have trouble with export. But either way, that did the trick! Thanks a bunch. May 28, 2020 at 16:58

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.