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)