Google Earth Engine – How to Perform Classification Using Google Earth Engine JavaScript API

classificationgoogle-earth-enginegoogle-earth-engine-javascript-api

I am trying to classify the Bostanliq area of Uzbekistan. Everything is fine unless my final result does not give Forest even though I collected many samples for that class of land.

My code:

var countries = ee.FeatureCollection("FAO/GAUL/2015/level2");
var dem = ee.ImageCollection('JAXA/ALOS/AW3D30/V3_2');
var elevation = dem.select('DSM');
var uzbekistan = countries.filter(ee.Filter.eq('ADM0_NAME','Uzbekistan'));

var bostanliq =uzbekistan.filter(ee.Filter.eq('ADM2_NAME','Bostanlik district'));
Map.addLayer(bostanliq, {}, "Bostanliq");

                                    ///2013///
var landsat8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    .filterDate('2014-05-01', '2015-06-01');

// Applies scaling factors.
function applyScaleFactors(image) {
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
  return image.addBands(opticalBands, null, true)
              .addBands(thermalBands, null, true);
}

var landsat8 = landsat8.map(applyScaleFactors);

var landsat_median = landsat8.median()
                            .clip(bostanliq)
                            .select(['SR_B1', 'SR_B2', 'SR_B3',
                            'SR_B4', 'SR_B5', 'SR_B7', 'SR_B6']);

var visualization = {
  bands: ['SR_B4', 'SR_B3', 'SR_B2'],
  min: 0.0,
  max: 0.3,
};

Map.addLayer(landsat_median,visualization, "Landsat 2013");

var gcp2013 =water.merge(glaciers).merge(urban).merge(rocks).merge(bare_land).merge(forests).merge(rangelands).merge(agriculture);
// Add a random column and split the GCPs into training and validation set
var gcp2013 = gcp2013.randomColumn();

//70 % training, 30 % 
var trainingGcp2013 =gcp2013.filter(ee.Filter.lt('random',0.7));
var validationGcp2013 =gcp2013.filter(ee.Filter.gte('random',0.7));


var training2013 = landsat_median.sampleRegions({
  collection:gcp2013,
  properties: ['landcover'],
  scale:30,
  tileScale:16
});

var classifier = ee.Classifier.smileRandomForest(50).train({
  features: training2013,
  classProperty:'landcover',
  inputProperties:landsat_median.bandNames(),
});

var classified2013 =landsat_median.classify(classifier);
Map.addLayer(classified2013, {min:1, max:8, palette:['blue','white','red','yellow','#c48c10','green','#00ff00','#ff64fd']}, "Classified");

var test =classified2013.sampleRegions({
  collection:validationGcp2013,
  properties:['landcover'],
   tileScale: 16,
   scale: 30,
});

var testConfusionMatrix2013 = test.errorMatrix('landcover', 'classification');
// Printing of confusion matrix may time out. Alternatively, you can export it as CSV
print('Confusion Matrix', testConfusionMatrix2013);
print('Test Accuracy', testConfusionMatrix2013.accuracy());

Export.image.toDrive({
  image: classified2013,
  description: 'Classification_2013',
  scale: 30,
  folder:'PhD',
  region: bostanliq,
  maxPixels: 1e13,
});

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

enter image description here

Best Answer

Everything is not fine because there are several issues in your code.

1.- First of all, as well pointed out Padmanabha, you have to assign class values in corresponding order starting on 0 (water = 0, glaciers = 1 and so on). This can be easily fixed in above code with functions as:

water = water.map(function (feat){
  
  return feat.set('landcover', 0);
});

However, you can better do that fixing your original feature collections.

2.- According to above numeral, you need to fix merging order as:

var gcp2013 = water.merge(glaciers)
                   .merge(urban)
                   .merge(agriculture)
                   .merge(bare_land)
                   .merge(forests)
                   .merge(rangelands)
                   .merge(rocks);

instead of this:

var gcp2013 =water.merge(glaciers).merge(urban).merge(rocks).merge(bare_land).merge(forests).merge(rangelands).merge(agriculture);

3.- For better observing above changes, I used following visualization parameters for classified layer:

Map.addLayer(classified2013, {min:0, 
                              max:7, 
                              palette:['blue',    //water
                                       'white',   //glaciers
                                       'red',     //urban
                                       'yellow',  //agriculture
                                       'brown',   //bare_land
                                       'green',   //forests
                                       'pink',    //rangelands
                                       'gray'     //rocks
                                       ]}, "Classified");

After running complete code at GEE code editor, I got the result of following picture:

enter image description here

Now, forests apparently look OK, however, the shadow of the slopes in high mountain conditions are making that some parts of the glaciers are being classified as human settlements. You should review this anomaly.

You can improve training points with this another script by using a COPERNICUS Landcover product.