0

The paper

Murray, N.J., Phinn, S.R., DeWitt, M., Ferrari, R., Johnston, R., Lyons, M.B., Clinton, N., Thau, D. & Fuller, R.A. (2019) The global distribution and trajectory of tidal flats. Nature, 565, 222-225.

gives a way to extract the tidal flat from landsat 4/5/7/8 images.

At the end of the paper, they have also given a piece of code:

//////////////////////////////////////////// 
// See Murray et al. (Nature, 2018) for further information.
// Developed in Google Earth Engine:
// https://code.earthengine.google.com
//////////////////////////////////////////// 

// 0. Global Variables
var site = ee.Geometry.Polygon([-180, 60, 0, 60, 180, 60, 180, -60, 10, -60, -180, -60], null, false);
var globOptions = { 
  versionID: '_SR',
  outFolder: 'SR',
  startDate: '2014-01-01',
  endDate: '2016-12-31',
  bandSelect: ['green', 'swir1', 'swir2', 'nir', 'red'],
  bands8: ['B3', 'B6', 'B7', 'B5', 'B4'],
  bands7: ['B2', 'B5', 'B7', 'B4', 'B3'], 
  maskAltitude: 100,  
  maskDepth: -100, 
  maskDistance: 50000,
  maskApplySRTM: false,
  parallelScale: 8,
  trainingValidationRatio: 0.0001, 
  nTrees: 10, 
  outScale: 30, 
  conPixels: 100
};

// 1. Functions
var landsatFunctions = {
  applyFMask: function(image) {
    // Mask out SHADOW, SNOW, and CLOUD classes. SR data.
    return image
      .updateMask(image.select('cfmask')
      .lt(2)); 
  },

  applyNDWI: function(image) {
    // apply NDWI to an image
    var ndwi = image.normalizedDifference(['green','nir']);
    return ndwi.select([0], ['ndwi']);
  },

  applyMNDWI: function(image) {
    // apply MNDWI to an image
    var mndwi = image.normalizedDifference(["green","swir1"]);
    return image.select([0], ['mndwi']);
  },

  applyAWEI: function(image) {
    // apply AWEI to an image
    var awei = image.expression("4*(b('green')-b('swir1'))-(0.25*b('nir')+2.75*b('swir2'))");
    return awei.select([0], ['awei']);
  },

  applyNDVI: function(image) {
    // apply NDVI to an image
    var ndvi = image.normalizedDifference(['nir','red']);
    return ndvi.select([0], ['ndvi']);
  },
};

var reducer = ee.Reducer.min()
    .combine(ee.Reducer.max(), '', true)
    .combine(ee.Reducer.stdDev(), '', true)
    .combine(ee.Reducer.median(), '', true)
    //.combine(ee.Reducer.count(), '', true) 
    .combine(ee.Reducer.percentile([10, 25, 50, 75,90]), '', true)
    .combine(ee.Reducer.intervalMean(0, 10).setOutputs(['intMn0010']), '', true)
    .combine(ee.Reducer.intervalMean(10, 25).setOutputs(['intMn1025']), '', true)
    .combine(ee.Reducer.intervalMean(25, 50).setOutputs(['intMn2550']), '', true)
    .combine(ee.Reducer.intervalMean(50, 75).setOutputs(['intMn5075']), '', true)
    .combine(ee.Reducer.intervalMean(75, 90).setOutputs(['intMn7590']), '', true)
    .combine(ee.Reducer.intervalMean(90, 100).setOutputs(['intMn90100']), '', true)
    .combine(ee.Reducer.intervalMean(10, 90).setOutputs(['intMn1090']), '', true)
    .combine(ee.Reducer.intervalMean(25, 75).setOutputs(['intMn2575']), '', true);

// 2. Data Imports & Processing
// vectors
var globCoast = ee.FeatureCollection('ft:1Hsoe_WwULJ23Nuj1wikGzfH_WQMtpDWOR3XpWkHk');
var randomPointsPreComputed = ee.FeatureCollection('ft:1hVC5uIlWZQxtsapNsr5AzcLm1Vzo4I_DqjfNgNmN'); // Precomputed Training

// images
function generateLandsatCollection(){
  var L4collection = ee.ImageCollection('LANDSAT/LT4_SR')
      .filterDate(globOptions.startDate, globOptions.endDate)
      .map(landsatFunctions.applyFMask)
      .select(globOptions.bands7, globOptions.bandSelect);
  var L5collection = ee.ImageCollection('LANDSAT/LT5_SR')
      .filterDate(globOptions.startDate, globOptions.endDate)
      .map(landsatFunctions.applyFMask)
      .select(globOptions.bands7, globOptions.bandSelect);
  var L7collection = ee.ImageCollection('LANDSAT/LE7_SR')
      .filterDate(globOptions.startDate,globOptions.endDate)
      .map(landsatFunctions.applyFMask)
      .select(globOptions.bands7, globOptions.bandSelect);
  var L8collection = ee.ImageCollection('LANDSAT/LC8_SR')
      .filterDate(globOptions.startDate, globOptions.endDate)
      .map(landsatFunctions.applyFMask)
      .select(globOptions.bands8, globOptions.bandSelect);
  var collectionFull = ee.ImageCollection(L4collection
      .merge(L5collection)
      .merge(L7collection)
      .merge(L8collection))
      //.filterBounds(site)
      .filter(ee.Filter.intersects('.geo', globCoast.geometry(), null, null, 1000))
      .filterMetadata('WRS_ROW', 'less_than', 120); 
  return collectionFull;
}
var collection = generateLandsatCollection();

// Data processing
var covariates = {
    aweiReduced: collection.map(landsatFunctions.applyAWEI)
        .reduce(reducer, globOptions.parallelScale),
    ndwiReduced: collection.map(landsatFunctions.applyNDWI)
        .reduce(reducer, globOptions.parallelScale),
    mndwiReduced: collection.map(landsatFunctions.applyMNDWI)
        .reduce(reducer, globOptions.parallelScale),
    ndvi: collection.map(landsatFunctions.applyNDVI)
        .reduce(ee.Reducer.intervalMean(10, 90)
        .setOutputs(['intMn1090'])),
    nirBand: collection.select(['nir'])
        .reduce(ee.Reducer.intervalMean(10, 90)
        .setOutputs(['intMn1090'])),
    swir1Band: collection.select(['swir1'])
        .reduce(ee.Reducer.intervalMean(10, 90)
        .setOutputs(['intMn1090'])),
    etopo: ee.Image('NOAA/NGDC/ETOPO1')
        .select(['bedrock'], ['etopo'])
        .resample('bicubic'),
    swOccurrence: ee.Image('JRC/GSW1_0/GlobalSurfaceWater')
        .select(['occurrence'], ['surfaceWater'])
        .unmask()
};
var trainComposite = covariates.aweiReduced
    .addBands(covariates.ndwiReduced)
    .addBands(covariates.mndwiReduced)
    .addBands(covariates.ndvi)
    .addBands(covariates.nirBand)
    .addBands(covariates.swir1Band)
    .addBands(covariates.etopo)
    .addBands(covariates.swOccurrence);
var bands = trainComposite.bandNames();

// 3. Masking
var coastMask = globCoast
    .distance(globOptions.maskDistance).gte(-20); 
var topoMask = covariates.etopo
    .gte(globOptions.maskDepth)
    .and(covariates.etopo.lte(globOptions.maskAltitude));
var topoMask = topoMask.updateMask(topoMask);
if (globOptions.maskApplySRTM) {
  var srtm = ee.Image('USGS/SRTMGL1_003')
    .lte(0);
  var finalMask = coastMask.multiply(topoMask)
    .rename('datamask')
    .byte()
    .updateMask(srtm);
} else {
  var finalMask = coastMask.multiply(topoMask)
    .rename('datamask')
    .byte();
}

// 4. Training Data
var predictorSet = randomPointsPreComputed;
var predictorSubSet = predictorSet
  .filter(ee.Filter.neq('ndwi_stdDev', null)) 
  .randomColumn('random', 0);
var trainingSet = predictorSubSet
  .filter(ee.Filter.gte('random',globOptions.trainingValidationRatio));
var validationSet = predictorSubSet
  .filter(ee.Filter.lt('random',globOptions.trainingValidationRatio));

// 5. Classify 
var classifier = ee.Classifier.randomForest({
    numberOfTrees: globOptions.nTrees, 
    variablesPerSplit: 0,
    bagFraction: 0.5,
    seed: 0})
  .train(trainingSet, 'CLASS', bands)
  .setOutputMode('CLASSIFICATION');
var classified = trainComposite.select(bands)
  .classify(classifier);
var finalOut = classified.byte()
  .mask(finalMask); 

// Postprocess
 var finalOut = finalOut.mask(finalOut
  .connectedPixelCount(globOptions.conPixels)
  .gte(globOptions.conPixels)); 

var finalOut = finalOut.updateMask(finalOut.eq(2)); // tf only


// Extra post-process
var terrestrial5k = ee.FeatureCollection("users/murrnick/tidalFlat/postProcessing/simBoundary_Buff5k");
var coastMask5k = ee.Image(1).clip(terrestrial5k);
var invcoastMask5k = coastMask5k.mask(coastMask5k.mask().not());
var finalOut = finalOut.updateMask(invcoastMask5k);

// 8. Run
print(ee.Serializer.toReadableJSON(finalOut));

This piece of code derives some information in the console window. I don't know what it is but anyways that's not what I want. I want to get the tidal flat maps so I edited the code, and my work is shown below:

//////////////////////////////////////////// 
// See Murray et al. (Nature, 2018) for further information.
// Developed in Google Earth Engine:
// https://code.earthengine.google.com
//////////////////////////////////////////// 

// 0. Global Variables
var site = ee.Geometry.Polygon([[-80.79, 24.98], [-80.08, 26.43], [-80.71, 26.16],[-81.07, 26.20]], null, false);
var globOptions = { 
  versionID: '_SR',
  outFolder: 'SR',
  startDate: '2014-01-01',
  endDate: '2016-12-31',
  bandSelect: ['blue', 'green', 'red', 'nir', 'swir1', 'swir2'],
  bands8: ['B2', 'B3', 'B4', 'B5', 'B6', 'B7'],
  bands7: ['B1', 'B2', 'B3', 'B4', 'B5', 'B7'], 
  maskAltitude: 100,  
  maskDepth: -100, 
  maskDistance: 50000,
  maskApplySRTM: false,
  parallelScale: 8,
  trainingValidationRatio: 0.0001,
  nTrees: 10, 
  outScale: 30, 
  conPixels: 100
};

// 1. Functions
var landsatFunctions = {
   //maskClouds: function(image) {
  //var cs = cloud_shadows(image);
  //var c = clouds(image);
  //image = image.updateMask(cs);
  //return image.updateMask(c);
  //},

  applyNDWI: function(image) {
    // apply NDWI to an image
    var ndwi = image.normalizedDifference(['green','nir']);
    return ndwi.select([0], ['ndwi']);
  },

  applyMNDWI: function(image) {
    // apply MNDWI to an image
    var mndwi = image.normalizedDifference(['green','swir1']);
    return image.select([0], ['mndwi']);
  },

  applyAWEI: function(image) {
    // apply AWEI to an image
    var awei = image.expression("b('blue')+2.5*b('green')-1.5*(b('nir')+b('swir1'))-0.25*b('swir2')");
    return awei.select([0], ['awei']);
  },

  applyNDVI: function(image) {
    // apply NDVI to an image
    var ndvi = image.normalizedDifference(['nir','red']);
    return ndvi.select([0], ['ndvi']);
  },
};

var getQABits = function(image, start, end, newName) {
    // Compute the bits we need to extract.
    var pattern = 0;
    for (var i = start; i <= end; i++) {
       pattern += Math.pow(2, i);
    }
    // Return a single band image of the extracted QA bits, giving the band
    // a new name.
    return image.select([0], [newName])
                  .bitwiseAnd(pattern)
                  .rightShift(start);
  };

  // A function to mask out cloudy shadow pixels.
var cloud_shadows = function(image) {
  // Select the QA band.
  var QA = image.select(['pixel_qa']);
  // Get the internal_cloud_algorithm_flag bit.
  return getQABits(QA, 3,3, 'Cloud_shadows').eq(0);
      // Return an image masking out cloudy areas.
  };

  // A function to mask out cloud pixels.
var clouds = function(image) {
  // Select the QA band.
  var QA = image.select(['pixel_qa']);
  // Get the internal_cloud_algorithm_flag bit.
  return getQABits(QA, 5,5, 'Cloud').eq(0);
  // Return an image masking out cloudy areas.
};

var maskClouds = function(image) {
  var cs = cloud_shadows(image);
  var c = clouds(image);
  image = image.updateMask(cs);
  return image.updateMask(c);
  };

var reducer = ee.Reducer.min()
    .combine(ee.Reducer.max(), '', true)
    .combine(ee.Reducer.stdDev(), '', true)
    .combine(ee.Reducer.median(), '', true)
    //.combine(ee.Reducer.count(), '', true) 
    .combine(ee.Reducer.percentile([10, 25, 50, 75,90]), '', true)
    .combine(ee.Reducer.intervalMean(0, 10).setOutputs(['intMn0010']), '', true)
    .combine(ee.Reducer.intervalMean(10, 25).setOutputs(['intMn1025']), '', true)
    .combine(ee.Reducer.intervalMean(25, 50).setOutputs(['intMn2550']), '', true)
    .combine(ee.Reducer.intervalMean(50, 75).setOutputs(['intMn5075']), '', true)
    .combine(ee.Reducer.intervalMean(75, 90).setOutputs(['intMn7590']), '', true)
    .combine(ee.Reducer.intervalMean(90, 100).setOutputs(['intMn90100']), '', true)
    .combine(ee.Reducer.intervalMean(10, 90).setOutputs(['intMn1090']), '', true)
    .combine(ee.Reducer.intervalMean(25, 75).setOutputs(['intMn2575']), '', true);

// 2. Data Imports & Processing
// vectors
var globCoast = ee.FeatureCollection('ft:1Hsoe_WwULJ23Nuj1wikGzfH_WQMtpDWOR3XpWkHk');
var randomPointsPreComputed = ee.FeatureCollection('ft:1hVC5uIlWZQxtsapNsr5AzcLm1Vzo4I_DqjfNgNmN'); // Precomputed Training

// images
  var L4collection = ee.ImageCollection('LANDSAT/LT04/C01/T1_SR')
      .filterDate(globOptions.startDate, globOptions.endDate)
      .map(maskClouds)
      .median()
      .select(globOptions.bands7, globOptions.bandSelect);
  var L5collection = ee.ImageCollection('LANDSAT/LT05/C01/T1_SR')
      .filterDate(globOptions.startDate, globOptions.endDate)
      .map(maskClouds)
      .median()
      .select(globOptions.bands7, globOptions.bandSelect);
  var L7collection = ee.ImageCollection('LANDSAT/LE07/C01/T1_SR')
      .filterDate(globOptions.startDate,globOptions.endDate)
      .map(maskClouds)
      .median()
      .select(globOptions.bands7, globOptions.bandSelect);
  var L8collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
      .filterDate(globOptions.startDate, globOptions.endDate)
      .map(maskClouds)
      .median()
      .select(globOptions.bands8, globOptions.bandSelect);
  var collectionFull = ee.ImageCollection(L4collection
  .merge(L5collection)
  .merge(L7collection)
  .merge(L8collection))
  //.filterBounds(site)
  .filter(ee.Filter.intersects('.geo', globCoast.geometry(), null, null, 1000))
  .filterMetadata('WRS_ROW', 'less_than', 120);

// Data processing
var covariates = {
    aweiReduced: collectionFull.map(landsatFunctions.applyAWEI)
        .reduce(reducer, globOptions.parallelScale),
    ndwiReduced: collectionFull.map(landsatFunctions.applyNDWI)
        .reduce(reducer, globOptions.parallelScale),
    mndwiReduced: collectionFull.map(landsatFunctions.applyMNDWI)
        .reduce(reducer, globOptions.parallelScale),
    ndvi: collectionFull.map(landsatFunctions.applyNDVI)
        .reduce(ee.Reducer.intervalMean(10, 90)
        .setOutputs(['intMn1090'])),
    nirBand: collectionFull.select(['nir'])
        .reduce(ee.Reducer.intervalMean(10, 90)
        .setOutputs(['intMn1090'])),
    swir1Band: collectionFull.select(['swir1'])
        .reduce(ee.Reducer.intervalMean(10, 90)
        .setOutputs(['intMn1090'])),
    etopo: ee.Image('NOAA/NGDC/ETOPO1')
        .select(['bedrock'], ['etopo'])
        .resample('bicubic'),
    swOccurrence: ee.Image('JRC/GSW1_0/GlobalSurfaceWater')
        .select(['occurrence'], ['surfaceWater'])
        .unmask()
};
var trainComposite = covariates.aweiReduced
    .addBands(covariates.ndwiReduced)
    .addBands(covariates.mndwiReduced)
    .addBands(covariates.ndvi)
    .addBands(covariates.nirBand)
    .addBands(covariates.swir1Band)
    .addBands(covariates.etopo)
    .addBands(covariates.swOccurrence);
var bands = trainComposite.bandNames();

// 3. Masking
var coastMask = globCoast
    .distance(globOptions.maskDistance).gte(-20); 
var topoMask = covariates.etopo
    .gte(globOptions.maskDepth)
    .and(covariates.etopo.lte(globOptions.maskAltitude));
var topoMask = topoMask.updateMask(topoMask);
if (globOptions.maskApplySRTM) {
  var srtm = ee.Image('USGS/SRTMGL1_003')
    .lte(0);
  var finalMask = coastMask.multiply(topoMask)
    .rename('datamask')
    .byte()
    .updateMask(srtm);
} else {
  var finalMask = coastMask.multiply(topoMask)
    .rename('datamask')
    .byte();
}

// 4. Training Data
var predictorSet = randomPointsPreComputed;
var predictorSubSet = predictorSet
  .filter(ee.Filter.neq('ndwi_stdDev', null)) 
  .randomColumn('random', 0);
var trainingSet = predictorSubSet
  .filter(ee.Filter.gte('random',globOptions.trainingValidationRatio));
var validationSet = predictorSubSet
  .filter(ee.Filter.lt('random',globOptions.trainingValidationRatio));

// 5. Classify 
var classifier = ee.Classifier.randomForest({
    numberOfTrees: globOptions.nTrees, 
    variablesPerSplit: 0,
    bagFraction: 0.5,
    seed: 0})
  .train(trainingSet, 'CLASS', bands)
  .setOutputMode('CLASSIFICATION');
var classified = trainComposite.select(bands)
  .classify(classifier);
var finalOut = classified.byte()
  .mask(finalMask); 

// Postprocess
 var finalOut = finalOut.mask(finalOut
  .connectedPixelCount(globOptions.conPixels)
  .gte(globOptions.conPixels)); 

var finalOut = finalOut.updateMask(finalOut.eq(2)); // tf only


// Extra post-process

//var coastMask5k = ee.Image(1).clip(terrestrial5k);
//var invcoastMask5k = coastMask5k.mask(coastMask5k.mask().not());
//var finalOut = finalOut.updateMask(invcoastMask5k);

// 8. Run
var finalOutViz = {min: 0, max: 1, palette: ['0000FF', 'FF0000']};
Map.setCenter(-80.702898,25.679568, 8);
Map.addLayer(finalOut, finalOutViz, 'Tidal Flats', false);

At this point, I get stuck. The error information says:"L4collection.merge is not a function." I really can't understand why since it works in the original code.

Can anyone help me to figure it out?

If you think my edition goes into a wrong direction and you could help me to get the tidal flat map in your own way, let me know as well.

  • 1
    L4collection is an image, not an image collection in your code. The median method returns an image from an image collection. – Kevin Jul 21 '19 at 10:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.