google-earth-engine – How to Export Filtered ImageCollection to Google Drive

google-earth-enginesentinel-2

I want to export a filtered Sentinel2 composite to the drive, but I get an error message.

Error: Exported bands must have compatible data types; found inconsistent types: Float32 and Float64.

In the topic inconsistent types in google earth engine with Export.image.toDrive() it is stated, that GEE does not support exporting images with mixed band types for the GeoTIFF format. It is suggested to use ee.Image.float(). But since I have an ImageCollection, I can not use this.

How do I convert all bands of a ImageCollection into float?

https://code.earthengine.google.com/cbcdaa0858e647504b85907857fe738c

// Define Region of Interest (ROI) for filtering the Sentinel data 
var ROI = /* color: #0b4a8b */ee.Geometry.Polygon([
      [36.5048217773437, 0.560293804172086],
      [37.9302978515625, 0.560293804172086],
      [37.9302978515625, 1.875580463973453],
      [36.5048217773437, 1.875580463973453]
  ]);
Map.centerObject(ROI,8);  

// Define temporal space to collect data from
// Dry Seasons Kenya January-February and June-August
// Define the maximum cloudiness of the pictures for later masking

var startdate = '2016-01-01'; // first image year-month-day
var enddate = '2017-12-31'; // last image year-month-day
var calendar = 'month'; // month','day_of_year' 
var startshortdry = 1; // start month of first season
var endshortdry = 2; // end month of first season
var startlongdry = 6; // start motnh of second season
var endlongdry = 8; // end moonth of second season
var startshortwet = endshortdry+1; // start month of first season
var endshortwet = startlongdry-1; // end month of first season
var startlongwet = endlongdry+1; // start motnh of second season
var endlongwet = 12; // end month of second season
var cloudiness = 5; // cloudiness in %

// Import Sentinel data representing the ROI as ImageCollectoin and filter by date
var s2_ROIData = ee.ImageCollection('COPERNICUS/S2')
  .filterBounds(ROI)
  .filterDate(startdate, enddate)
  .filter(ee.Filter.or(ee.Filter.calendarRange(startshortdry, endshortdry, calendar),
  ee.Filter.calendarRange(startlongdry, endlongdry, calendar)))
  .map(function(image) {return image.clip(ROI);})
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', cloudiness)
  );

// Convert the collection to a list and get the number of images
var count = s2_ROIData.size();
print('Number of filtered images in the region of interest: ', count);

// Function to remove clouds from the image collection. 
// The function uses the Sentinel-2 QA60 Band, 
// where bit10 represents clouds and bit11 represents cirrus
// Flags set to 0 indicating clear conditions. Returns masked and scaled data
   function ROIData_cloudmask(image) {
  var qa60 = image.select('QA60');
  var cloudBitMask = ee.Number(2).pow(10).int();
  var cirrusBitMask = ee.Number(2).pow(11).int();
  var mask =     qa60.bitwiseAnd(cloudBitMask).eq(0).and(qa60.bitwiseAnd(cirrusBitMask).eq(0));
return image.updateMask(mask).divide(10000);
  }

// Function to calculate MSAVI 
// MSAVI = (2*NIR+1-√((2*NIR+1)^2 - 8*(NIR-RED)))/2 
// MSAVI2 = (B08 + 1) - 0.5 * sqrt((2 * B08 - 1) ^ 2 + 8 * B04)) other Formula
    //     http://wiki.landscapetoolbox.org/doku.php/remote_sensing_methods:modified_soil-adjusted_vegetation_index
function ROIData_addMSAVI(input) {
      var msavi = input.select('B8').multiply(2).add(1)
      .subtract(input.select('B8').multiply(2).add(1).pow(2)
      .subtract(input.select('B8').subtract(input.select('B4'))
      .multiply(8)).sqrt()).divide(2)
      .rename('MSAVI');
   return input.addBands(msavi);
  }

// Function to calculate the Bare Soil Index BSI of the collection
//BSI = {(B11+B4)-(B8+B2)}/{(B11+B4)+(B8+B2)}
function ROIData_addBSI(input) {
  var bsi = input.select('B11').add(input.select('B4'))
      .subtract(input.select('B8').add(input.select('B2')))
          .divide(input.select('B1').add(input.select('B4')).add(input.select('B8')).add(input.select('B2')))
      .rename('BSI');
return input.addBands(bsi);
  }

// Function to calculate NDVI of the collection
// NDVI = (NIR - RED) / (NIR + RED), where RED is B4 and NIR is B8
// Values of 1 show vegetation, 0 bare and -1 water
function ROIData_addNDVI(input) {
  var ndvi = input.normalizedDifference(['B8', 'B4']).rename('NDVI');
return input.addBands(ndvi);
  }

// Pre-filter the composit for less cloudy granules and take the median
var ROIData_composite = s2_ROIData
  .map(ROIData_cloudmask)
  .map(ROIData_addMSAVI)
  .map(ROIData_addBSI)
  .map(ROIData_addNDVI)
  .median();

// Display cloudfree composit and Indices of the ROI 
// Values on Indices rangeing from -1 to 1, min and max adjusted for better visualization
//Map.addLayer(ROIData_composite, {bands:['MSAVI'], min: -0.5, max: 0.5, palette: ['blue','white', 'green']}, 'MSAVI');
//Map.addLayer(ROIData_composite, {bands:['BSI'], min: -0.5, max: 0.5, palette: ['blue','white', 'red']}, 'BSI');
Map.addLayer(ROIData_composite, {bands: ['B4', 'B3', 'B2'], min: 0, max: 0.2},'Cloudfree');

// Export the ROI satellite imagery in the Tasks tab
 Export.image.toDrive({
  image: ROIData_composite,
  description: 'Sentinel2_Composite',
  scale: 10,
  maxPixels: 3784216672400,
  region: ROI,
  });

Best Answer

In your code you are not exporting an ImageCollection but a single image. ROIData_composite is the median image of the ImageCollection s2_ROIData.

You can use .float() in your export statement and the export works:

// Export the ROI satellite imagery in the Tasks tab
 Export.image.toDrive({
  image: ROIData_composite.float(),
  description: 'Sentinel2_Composite',
  scale: 10,
  maxPixels: 3784216672400,
  region: ROI,
  });
Related Question