Specify thresholds for each land cover class
step 1: add the image and primitives
var chl = ee.FeatureCollection("users/servirmekong/Vietnam/CHL_Boundary").geometry();
var plantations = ee.Image("users/servirmekong/Vietnam/primitives/plantations").rename('plantations')
var forest = ee.Image("users/servirmekong/Vietnam/primitives/forest").rename('forest')
var cropland = ee.Image("users/servirmekong/Vietnam/primitives/cropland").rename('cropland')
var urban = ee.Image("users/servirmekong/Vietnam/primitives/urban").rename('settlement')
var water = ee.Image("users/servirmekong/Vietnam/primitives/water").rename('water')
step 2: add all images to a single image
var primiImg = plantations.addBands(forest).addBands(cropland).addBands(urban).addBands(water)
step 3: Create as list of classes and define the thresholds
var primitives = ['forest','plantations','settlement','cropland',"water"]; var defaultThresholds = [70, 70, 70, 70,70,70]; var year = 2017 // container for the classified landclass image var landClass = ee.Image();
step 4: add some ui objects
// ************************************************
// ui objects
// containers for sliders
var sliders = {};
// containers for inspected values
var valLabels = {};
// list of all selectable years
var availableYears = ['2017'];
// dropdown to select years
var yearList = ui.Select(availableYears, 'year', ''+year);
// checkbox to visualize primitives using thresholds
var primThresholds = ui.Checkbox({
label:'Visualize primitive by thresholds',
style:{'height':'18px','fontSize':'11px', 'padding':'4px', 'margin':'0px'}
});
// button to refresh display according to parameters
var refreshDisplay = ui.Button({
label:'Refresh Display',
onClick: refresh,
style: {'fontSize':'11px', 'padding':'4px', 'margin':'0px'}
});
// button to export the current LandCover
var exportLC = ui.Button({
label:'Export current landCover',
onClick: exportHelper,
style: {'fontSize':'11px', 'padding':'4px', 'margin':'0px'}
});
step 5: add function to reclassify the image
// function to reclassify stack image using decision tree
function reclassify(stackImage, decisionTree){
var classifier = ee.Classifier.decisionTree(decisionTree);
var landClass = stackImage.classify(classifier);
return landClass;
}
step 6: add function to select primitive.
// client side list map function to change list of primitives
// to a list of images corresponding to the primitives
// also adds the primitives to the map
function getPrimitiveImages(primitive){
var image = primiImg.select(primitive) //.multiply(0.01).rename(primitive);
var primLayer = ui.Map.Layer(image, {max:100}, primitive, false, 1);
if (primThresholds.getValue()){
primLayer = ui.Map.Layer(image.gte(sliders[primitive].getValue()), {}, primitive, false, 1);
}
Map.layers().set(primitives.indexOf(primitive), primLayer);
return image;
}
step 7: add the function to build the decision tree
// function to build decision tree from interface
function buildDecisionTree(){
print(sliders)
var values = {
forest:sliders['forest'].getValue(),
plantations:sliders['plantations'].getValue(),
settlement:sliders['settlement'].getValue(),
cropland:sliders['cropland'].getValue(),
water:sliders['water'].getValue()
}
var DT = ['1) root 9999 9999 9999']
var base = 1;
for (var i = 0; i ='+values[primitives[i]]+' 9999 9999 '+(i+1)+' *');
if(i == primitives.length-1){
DT.push(''+b+') '+primitives[i]+'<'+values[primitives[i]]+' 9999 9999 0 *');
}else DT.push(''+b+') '+primitives[i]+'<'+values[primitives[i]]+' 9999 9999 9999');
base = b
}
return DT.join('\n')
}
step 8: function to start the process
// function to start the process
function process(year){
var stackImage = ee.Image(primitives.map(getPrimitiveImages)) //.clip(nepalBounds);
landClass = reclassify(stackImage, buildDecisionTree());
var lcLayer = ui.Map.Layer(landClass.clip(chl), imageVisParam, 'land cover', true, 1);
Map.layers().set(primitives.length, lcLayer);
}
step 9: function to add the legend
// function to add legend to the map
function addLegend(){
var UTILS = require('users/khanalnishant/Algorithms:Utilities')
var palette = imageVisParam.palette
palette = palette.slice(1).concat(palette[0])
var labels = primitives.concat('otherland')
UTILS.addLegend(palette, labels,'Legend',{
'position':'bottom-right',
'padding':'8px 16px',
});
}
step 10: function to inspect the maps
// function to add inspect panel to the map
function addInspect(){
var panel = ui.Panel([], ui.Panel.Layout.Flow('vertical'),{maxHeight:'200px',position:'bottom-center'});
var inspectLabel = ui.Label('Click to inspect primitive values',{'height':'18px','fontSize':'11px', 'padding':'0px', 'margin':'1px'});
panel.add(inspectLabel)
for (var i = 0; i <primitives.length;i++){
var insLabel = ui.Label(primitives[i],{'width':'60px','fontSize':'11px', 'padding':'0px', 'margin':'1px'});
valLabels[primitives[i]] = ui.Label('Click Map',{'fontSize':'11px', 'padding':'0px', 'margin':'1px'});
var inspectSubPanel = ui.Panel([insLabel, valLabels[primitives[i]]], ui.Panel.Layout.Flow('horizontal'));
panel.add(inspectSubPanel);
}
Map.add(panel)
}
step 11: fetch the values when the map is clicked.
// function to update the inspect section once map is clicked
function fetchValues(lonlat){
var point = ee.Geometry.Point(lonlat.lon, lonlat.lat);
var stack = ee.Image([]);
for (var i=0; i<primitives.length;i++){
var image = Map.layers().get(i).get('eeObject');
stack = stack.addBands(image);
valLabels[primitives[i]].setValue('updating values');
}
var pointSampled = stack.sample(point,30).first().evaluate(updateValues);
}
step 12: add function to update labels
//update the labels once the sampling is done
function updateValues(feature){
// print(feature.properties);
for (var i=0; i<primitives.length;i++){
var value = parseFloat(feature.properties[primitives[i]]).toFixed(2)
valLabels[primitives[i]].setValue(value);
}
}
step 13: add function to export assemblage
// function to export current Land Cover
function exportHelper(){
Export.image.toAsset({
image:landClass.toInt8(),
description:'LandCover-'+year,
region:chl,
scale:30,
maxPixels:1e10
})
}
step 14: add function to refresh the map after setting parameters.
// function to refresh display based on parameters
function refresh(){
// get layer shown state
var layers = Map.layers();
var shownStat = layers.map(function(layer){
return layer.getShown();
});
// get selected year
year = yearList.getValue();
// initiate the process
process(year);
// reassign the previous layer shown status
layers = Map.layers();
layers.map(function(layer){
return layer.setShown(shownStat[layers.indexOf(layer)]);
});
}
step 15: function to initialize
// function to initialize the application
function init(){
var panel = ui.Panel([], ui.Panel.Layout.Flow('vertical'),{position:'bottom-left'});
var yearLabel = ui.Label('Year',{'width':'30px','height':'18px','fontSize':'11px', 'margin':'15px 0px'});
var yearSubPanel = ui.Panel([yearLabel, yearList], ui.Panel.Layout.Flow('horizontal'));
panel.add(yearSubPanel);
var sliderLabel = ui.Label('Adjust Probability Thresholds',{'height':'18px','fontSize':'11px', 'padding':'0px', 'margin':'1px'});
panel.add(sliderLabel);
for (var i = 0; i< primitives.length;i++){
var label = ui.Label(primitives[i],{'width':'50px','height':'18px','fontSize':'11px', 'padding':'0px', 'margin':'1px'});
sliders[primitives[i]] = ui.Slider({
min:0,
max:100,
value:defaultThresholds[i],
style:{'height':'18px','fontSize':'11px', 'padding':'0px', 'margin':'1px'}
});
var subPanel = ui.Panel([label, sliders[primitives[i]]], ui.Panel.Layout.Flow('horizontal'),{'padding':'4px'});
panel.add(subPanel);
}
panel.add(primThresholds);
panel.add(refreshDisplay);
panel.add(exportLC)
Map.add(panel);
addLegend();
addInspect();
Map.onClick(fetchValues);
}
step 16: initialize the tool.
init(); var dec = buildDecisionTree() print(dec) process(year)