Google Earth Engine – Calculate Transition of Different NDVI Classes

google-earth-enginegoogle-earth-engine-javascript-apiimage classificationndviremote sensing

My competency in GEE is quite low. I have calculated 2 NDVI layers (pre and post-disturbance NDVI). I have separated these NDVI layers into 4 classes according to NDVI values. Now I need to calculate the amount of area from each classes that transitioned into other classes from pre-NDVI to post-ndvi. Now i have no clue on how to do this part. Can anyone please advise me on how to proceed from the part I have finished?

My Script can be found here (https://code.earthengine.google.co.in/?scriptPath=users%2Fmd007%2FLearning_for_article_publication%3AGIS).

My assest (https://code.earthengine.google.co.in/?asset=users/md007/Sundaban_shapefile).

//var roi =(https://code.earthengine.google.co.in/?asset=users/md007/Sundaban_shapefile)
Map.centerObject(roi ,9);

//Created a water mask to work with the vegetative parts only.
var hansenImage = ee.Image('UMD/hansen/global_forest_change_2015');

// Select the land/water mask.
var datamask = hansenImage.select('datamask');

// Create a binary mask.
var watermask = datamask.eq(1);

//Cloud Masking
function prepSrL8(image) {
    var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
  var saturationMask = image.select('QA_RADSAT').eq(0);
  image.select('SR_QA_AEROSOL').gt(1).and(image.select('SR_QA_AEROSOL').lte(164));
  // Apply the scaling factors to the appropriate bands.
  var getFactorImg = function(factorNames) {
    var factorList = image.toDictionary().select(factorNames).values();
    return ee.Image.constant(factorList);
  };
  var scaleImg = getFactorImg([
    'REFLECTANCE_MULT_BAND_.|TEMPERATURE_MULT_BAND_ST_B10']);
  var offsetImg = getFactorImg([
    'REFLECTANCE_ADD_BAND_.|TEMPERATURE_ADD_BAND_ST_B10']);
  var scaled = image.select('SR_B.|ST_B10').multiply(scaleImg).add(offsetImg);
  // Replace original bands with scaled bands and apply masks.
  return image.addBands(scaled, null, true)
    .updateMask(qaMask).updateMask(saturationMask)
    // .updateMask(aeroMask);
  };

//'Pre Disaster NDVI'
// Landsat 8 Collection 2 surface reflectance for 'Pre Disaster NDVI'
var col = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
  .filterBounds(roi)
  .filterDate('2017-01-01', '2017-04-30')
  .filter(ee.Filter.lte('CLOUD_COVER',10));
  print(col);
var col = col
  .map(prepSrL8)
  .select('SR.*')
  .reduce(ee.Reducer.median())
  .updateMask(watermask)
  .clip(roi);

// Display the cloud-free median composite.
var visParams = {
  bands: ['SR_B4_median', 'SR_B3_median', 'SR_B2_median'],
  min: 0,
  max: .1
};
Map.setCenter(89.409, 21.962, 8);
Map.addLayer(col.clip(roi), visParams, 'Cloud-free mosaic of Pre Disaster NDVI');

// Create some visualization parameters
var ndviParams = {min: -1, max: 1, palette: ['blue', 'white', 'green']};
var diffParams = {min: -2, max: 2, palette:['red','white','green']};

// Create an NDVI image using bands the NIR and red bands (5 and 4)
var Pre_NDVI = col.normalizedDifference(['SR_B5_median', 'SR_B4_median']);
Map.addLayer(Pre_NDVI, ndviParams, "Pre Disaster NDVI");

//Classify Pre disaster NDVI into 5 classes
var ndvi1 = ee.Image(1)
          .where(Pre_NDVI.gt(0.0).and(Pre_NDVI.lte(0.2)), 1)
          .where(Pre_NDVI.gt(0.2).and(Pre_NDVI.lte(0.4)), 2)
          .where(Pre_NDVI.gt(0.4).and(Pre_NDVI.lte(0.6)), 3)
          .where(Pre_NDVI.gt(0.6), 4)
var ndvi1 = ndvi1.clip(roi).updateMask(watermask);

// Add map layers
Map.addLayer(ndvi1, {min: 1, max: 4, palette: ['#FFA500','#FFFF00', '#00FF00', '#008000']}, 'Pre Classified NDVI',true);

//Area calcualtion of each NDVI classess 
var areaImage1 = ee.Image.pixelArea().divide(1e6).addBands(ndvi1).updateMask(watermask);

// Using a Grouped Reducer
var areas1 = areaImage1.reduceRegion({
      reducer: ee.Reducer.sum().group({
      groupField: 1,
      groupName: 'classification',
    }),
    geometry: roi,
    scale: 100,
    tileScale: 4,
    maxPixels: 1e10
    });
var classAreas1 = ee.List(areas1.get('groups'))
print(classAreas1);

// Post Disaster NDVI
// Landsat 8 Collection 2 surface reflectance for 'Post Disaster NDVI'
var col1 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
  .filterBounds(roi)
  .filterDate('2018-01-01', '2018-04-30')
  .filter(ee.Filter.lte('CLOUD_COVER',10))
var col1 = col1
  .map(prepSrL8)
  .select('SR.*')
  .reduce(ee.Reducer.median())
  .updateMask(watermask)
  .clip(roi);

// Display the cloud-free median composite.
Map.setCenter(89.409, 21.962, 8);
Map.addLayer(col1.clip(roi), visParams, 'Cloud-free mosaic of Post Disaster NDVI');

// Create an Post Disaster NDVI image using bands the NIR and red bands (5 and 4)
var Post_NDVI = col1.normalizedDifference(['SR_B5_median', 'SR_B4_median']);
Map.addLayer(Post_NDVI, ndviParams, "Post Disaster NDVI");

// Classify the Post Disaster NDVI into 5 classes
var ndvi2 = ee.Image(1)
          .where(Post_NDVI.gt(0.0).and(Post_NDVI.lte(0.2)), 1)
          .where(Post_NDVI.gt(0.2).and(Post_NDVI.lte(0.4)), 2)
          .where(Post_NDVI.gt(0.4).and(Post_NDVI.lte(0.6)), 3)
          .where(Post_NDVI.gt(0.6), 4)
var ndvi2 = ndvi2.clip(roi).updateMask(watermask);

// Add map layers
Map.addLayer(ndvi2, {min: 1, max: 4, palette: ['#FFA500','#FFFF00', '#00FF00', '#008000']}, 'Post Classified NDVI',true);

//Area calcualtion of each NDVI classess 
var areaImage2 = ee.Image.pixelArea().divide(1e6).addBands(ndvi2).updateMask(watermask);

// Using a Grouped Reducer
var areas2 = areaImage2.reduceRegion({
      reducer: ee.Reducer.sum().group({
      groupField: 1,
      groupName: 'classification',
    }),
    geometry: roi,
    scale: 100,
    tileScale: 4,
    maxPixels: 1e10
    });

var classAreas2 = ee.List(areas2.get('groups'))
print(classAreas2)```

Best Answer

There are two steps embedded in your question. Step one is to identify all pixels of a given pre-event NDVI class that changed to a different NDVI class after the event. Step two is to calculate the total area of those pixels.

Step one: You can do this by starting with making a binary map of changed pixels (look for all pixels where you saw a change in NDVI), then looking for pixels that have a value of 1 and that also overlap with a given NDVI class.

var changes = ee.Image(0).where(ndvi1.eq(ndvi2).not(),1)
var just1 = ee.Image(0).where(ndvi1.eq(1).and(changes.eq(1)),1)
var just2 = ee.Image(0).where(ndvi1.eq(2).and(changes.eq(1)),1)
var just3 = ee.Image(0).where(ndvi1.eq(3).and(changes.eq(1)),1)
var just4 = ee.Image(0).where(ndvi1.eq(4).and(changes.eq(1)),1)

Step two: You can do this using ee.Image.pixelArea(), which returns pixel areas as an image object. Since the map of changed pixels is binary, you can multiply it by the pixelArea map to get areas of changed pixels, then use a sum Reducer to add up all that area within your ROI:

var areas1 = just1.multiply(ee.Image.pixelArea())
var areas2 = just2.multiply(ee.Image.pixelArea())
var areas3 = just3.multiply(ee.Image.pixelArea())
var areas4 = just4.multiply(ee.Image.pixelArea())



Map.addLayer(areas1, {}, 'areas1')
var changed1 = areas1.reduceRegion({
  reducer: ee.Reducer.sum(),
  geometry: roi.geometry(),
  scale: 5,
  maxPixels: 1e9
});
print(changed1)

The output (changed1) will give you the total area (in square meters) of pixels that used to be class 1 NDVI, but are now not class 1 NDVI. You could do that for all your classes. (Or, if you want, you could use a grouped reducer. This link has some good info about doing that.)

(Also, I would not say that your GEE competency is low, for what it's worth. I would not say this is a beginner-level script, and if you wrote it, or even if someone else wrote it and you're able to manipulate it, that's a pretty high level of competency.)

Related Question