diff --git a/code/end_to_end_gee/02-Earth-Engine-Intermediate/03b_Computation_on_Image_Collections_(complete) b/code/end_to_end_gee/02-Earth-Engine-Intermediate/03b_Computation_on_Image_Collections_(complete) index 14d6d484..f838863f 100644 --- a/code/end_to_end_gee/02-Earth-Engine-Intermediate/03b_Computation_on_Image_Collections_(complete) +++ b/code/end_to_end_gee/02-Earth-Engine-Intermediate/03b_Computation_on_Image_Collections_(complete) @@ -24,7 +24,7 @@ var withNdvi = filtered.map(addNDVI); var composite = withNdvi.median(); -var ndviComposite = composite.select('ndvi').clip(geometry); +var ndviComposite = composite.select('ndvi'); var palette = [ 'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', @@ -32,4 +32,4 @@ var palette = [ '004C00', '023B01', '012E01', '011D01', '011301']; var ndviVis = {min:0, max:0.5, palette: palette }; -Map.addLayer(ndviComposite, ndviVis, 'ndvi'); +Map.addLayer(ndviComposite.clip(geometry), ndviVis, 'ndvi'); diff --git a/code/end_to_end_gee/02-Earth-Engine-Intermediate/03c_Computation_on_Image_Collections_(exercise) b/code/end_to_end_gee/02-Earth-Engine-Intermediate/03c_Computation_on_Image_Collections_(exercise) index 740b924c..28911c1a 100644 --- a/code/end_to_end_gee/02-Earth-Engine-Intermediate/03c_Computation_on_Image_Collections_(exercise) +++ b/code/end_to_end_gee/02-Earth-Engine-Intermediate/03c_Computation_on_Image_Collections_(exercise) @@ -25,10 +25,11 @@ function addIndices(image) { var withIndices = filtered.map(addIndices); // Composite -var composite = withIndices.median().clip(geometry); +var composite = withIndices.median(); print(composite); -// Extract the 'ndwi' band and display a NDWI map +// Extract the 'ndwi' band +// Clip and display a NDWI map // use the palette ['white', 'blue'] // Hint: Use .select() function to select a band diff --git a/code/end_to_end_gee/03-Supervised-Classification/01b_Basic_Supervised_Classification_(complete) b/code/end_to_end_gee/03-Supervised-Classification/01b_Basic_Supervised_Classification_(complete) index e4f706de..bd26c407 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/01b_Basic_Supervised_Classification_(complete) +++ b/code/end_to_end_gee/03-Supervised-Classification/01b_Basic_Supervised_Classification_(complete) @@ -229,10 +229,10 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); var gcps = urban.merge(bare).merge(water).merge(vegetation); @@ -258,6 +258,6 @@ Map.centerObject(geometry); // Urban, Bare, Water, Vegetation var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); diff --git a/code/end_to_end_gee/03-Supervised-Classification/01c_Basic_Supervised_Classification_(exercise) b/code/end_to_end_gee/03-Supervised-Classification/01c_Basic_Supervised_Classification_(exercise) index a80aabf7..f1afdb62 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/01c_Basic_Supervised_Classification_(exercise) +++ b/code/end_to_end_gee/03-Supervised-Classification/01c_Basic_Supervised_Classification_(exercise) @@ -16,12 +16,12 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. var rgbVis = {min: 0.0, max: 3000, bands: ['B4', 'B3', 'B2']}; -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Exercise // Add training points for 4 classes @@ -60,6 +60,6 @@ Map.addLayer(composite, rgbVis, 'image'); // // Urban, Bare, Water, Vegetation // var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -// Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +// Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); diff --git a/code/end_to_end_gee/03-Supervised-Classification/01d_Basic_Supervised_Classification_(noimport) b/code/end_to_end_gee/03-Supervised-Classification/01d_Basic_Supervised_Classification_(noimport) index 625e958f..a0399a8e 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/01d_Basic_Supervised_Classification_(noimport) +++ b/code/end_to_end_gee/03-Supervised-Classification/01d_Basic_Supervised_Classification_(noimport) @@ -15,7 +15,7 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. var rgbVis = { @@ -23,7 +23,7 @@ var rgbVis = { max: 3000, bands: ['B4', 'B3', 'B2'], }; -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); var gcps = urban.merge(bare).merge(water).merge(vegetation); @@ -48,7 +48,7 @@ var classified = composite.classify(classifier); // Urban, Bare, Water, Vegetation var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); // Display the GCPs // We use the style() function to style the GCPs var palette = ee.List(palette); diff --git a/code/end_to_end_gee/03-Supervised-Classification/02a_Accuracy_Assessment b/code/end_to_end_gee/03-Supervised-Classification/02a_Accuracy_Assessment index 3ee38ce6..65cfcaee 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/02a_Accuracy_Assessment +++ b/code/end_to_end_gee/03-Supervised-Classification/02a_Accuracy_Assessment @@ -19,10 +19,10 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Overlay the point on the image to get training data. @@ -44,4 +44,4 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); \ No newline at end of file +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); \ No newline at end of file diff --git a/code/end_to_end_gee/03-Supervised-Classification/02b_Accuracy_Assessment_(complete) b/code/end_to_end_gee/03-Supervised-Classification/02b_Accuracy_Assessment_(complete) index 6e35c908..49790ef2 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/02b_Accuracy_Assessment_(complete) +++ b/code/end_to_end_gee/03-Supervised-Classification/02b_Accuracy_Assessment_(complete) @@ -18,10 +18,10 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Add a random column and split the GCPs into training and validation set @@ -53,7 +53,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment //************************************************************************** diff --git a/code/end_to_end_gee/03-Supervised-Classification/03a_Improving_the_Classification b/code/end_to_end_gee/03-Supervised-Classification/03a_Improving_the_Classification index 793be4af..37c8b838 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/03a_Improving_the_Classification +++ b/code/end_to_end_gee/03-Supervised-Classification/03a_Improving_the_Classification @@ -20,10 +20,10 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry) +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Add a random column and split the GCPs into training and validation set @@ -55,7 +55,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment diff --git a/code/end_to_end_gee/03-Supervised-Classification/03b_Improving_the_Classification_(complete) b/code/end_to_end_gee/03-Supervised-Classification/03b_Improving_the_Classification_(complete) index 29b3ba71..cec7e292 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/03b_Improving_the_Classification_(complete) +++ b/code/end_to_end_gee/03-Supervised-Classification/03b_Improving_the_Classification_(complete) @@ -35,7 +35,7 @@ var filtered = s2 .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); var addIndices = function(image) { @@ -62,7 +62,7 @@ var slope = ee.Terrain.slope(alos.select('AVE_DSM')).rename('slope'); var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -130,7 +130,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment diff --git a/code/end_to_end_gee/03-Supervised-Classification/04a_Exporting_Classification_Results b/code/end_to_end_gee/03-Supervised-Classification/04a_Exporting_Classification_Results index d9a39441..7c6cdce0 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/04a_Exporting_Classification_Results +++ b/code/end_to_end_gee/03-Supervised-Classification/04a_Exporting_Classification_Results @@ -34,7 +34,7 @@ var filtered = s2 .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); var addIndices = function(image) { var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']); @@ -60,7 +60,7 @@ var slope = ee.Terrain.slope(alos.select('AVE_DSM')).rename('slope'); var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -129,7 +129,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment //************************************************************************** diff --git a/code/end_to_end_gee/03-Supervised-Classification/04b_Exporting_Classification_Results_(complete) b/code/end_to_end_gee/03-Supervised-Classification/04b_Exporting_Classification_Results_(complete) index 995ebdac..a62298de 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/04b_Exporting_Classification_Results_(complete) +++ b/code/end_to_end_gee/03-Supervised-Classification/04b_Exporting_Classification_Results_(complete) @@ -34,7 +34,7 @@ var filtered = s2 .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); var addIndices = function(image) { var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']); @@ -60,7 +60,7 @@ var slope = ee.Terrain.slope(alos.select('AVE_DSM')).rename('slope'); var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -129,7 +129,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment diff --git a/code/end_to_end_gee/03-Supervised-Classification/04c_Exporting_Classification_Results_(exercise) b/code/end_to_end_gee/03-Supervised-Classification/04c_Exporting_Classification_Results_(exercise) index 199b0c62..37fa0767 100644 --- a/code/end_to_end_gee/03-Supervised-Classification/04c_Exporting_Classification_Results_(exercise) +++ b/code/end_to_end_gee/03-Supervised-Classification/04c_Exporting_Classification_Results_(exercise) @@ -34,7 +34,7 @@ var filtered = s2 .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); var addIndices = function(image) { var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']); @@ -60,7 +60,7 @@ var slope = ee.Terrain.slope(alos.select('AVE_DSM')).rename('slope'); var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -129,7 +129,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); // Exercise // Use the Export.image.toAsset() function to export the diff --git a/code/end_to_end_gee/04-Change-Detection/03a_Classifying_Change b/code/end_to_end_gee/04-Change-Detection/03a_Classifying_Change index 96879db5..b28a249e 100644 --- a/code/end_to_end_gee/04-Change-Detection/03a_Classifying_Change +++ b/code/end_to_end_gee/04-Change-Detection/03a_Classifying_Change @@ -188,15 +188,15 @@ var filtered = s2 .map(maskS2clouds); -var image2019 = filtered.median().clip(geometry); +var image2019 = filtered.median(); // Display the input composite. -Map.addLayer(image2019, rgbVis, '2019'); +Map.addLayer(image2019.clip(geometry), rgbVis, '2019'); var filtered = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(bangalore)) .map(maskS2clouds) -var image2020 = filtered.median().clip(geometry); +var image2020 = filtered.median(); -Map.addLayer(image2020, rgbVis, '2020'); +Map.addLayer(image2020.clip(geometry), rgbVis, '2020'); diff --git a/code/end_to_end_gee/04-Change-Detection/03b_Classifying_Change_(complete) b/code/end_to_end_gee/04-Change-Detection/03b_Classifying_Change_(complete) index 3112faea..6fd62eaa 100644 --- a/code/end_to_end_gee/04-Change-Detection/03b_Classifying_Change_(complete) +++ b/code/end_to_end_gee/04-Change-Detection/03b_Classifying_Change_(complete) @@ -29,18 +29,18 @@ var filtered = s2 .map(maskS2clouds); -var image2019 = filtered.median().clip(geometry); +var image2019 = filtered.median(); // Display the input composite. -Map.addLayer(image2019, rgbVis, '2019'); +Map.addLayer(image2019.clip(geometry), rgbVis, '2019'); var filtered = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(bangalore)) .map(maskS2clouds); -var image2020 = filtered.median().clip(geometry); +var image2020 = filtered.median(); -Map.addLayer(image2020, rgbVis, '2020'); +Map.addLayer(image2020.clip(geometry), rgbVis, '2020'); var stackedImage = image2019.addBands(image2020); @@ -60,4 +60,4 @@ var classifier = ee.Classifier.smileRandomForest(50).train({ // Classify the image. var classified = stackedImage.classify(classifier); -Map.addLayer(classified, {min: 0, max: 1, palette: ['white', 'red']}, 'change'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 1, palette: ['white', 'red']}, 'change'); diff --git a/code/end_to_end_gee/04-Change-Detection/03c_Classifying_Change_(exercise) b/code/end_to_end_gee/04-Change-Detection/03c_Classifying_Change_(exercise) index 695d48b4..1fb6a974 100644 --- a/code/end_to_end_gee/04-Change-Detection/03c_Classifying_Change_(exercise) +++ b/code/end_to_end_gee/04-Change-Detection/03c_Classifying_Change_(exercise) @@ -30,18 +30,18 @@ var filtered = s2 .map(maskS2clouds); -var image2019 = filtered.median().clip(geometry); +var image2019 = filtered.median(); // Display the input composite. -Map.addLayer(image2019, rgbVis, '2019'); +Map.addLayer(image2019.clip(geometry), rgbVis, '2019'); var filtered = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(bangalore)) .map(maskS2clouds); -var image2020 = filtered.median().clip(geometry); +var image2020 = filtered.median(); -Map.addLayer(image2020, rgbVis, '2020'); +Map.addLayer(image2020.clip(geometry), rgbVis, '2020'); // Exercise @@ -74,4 +74,4 @@ var classifier = ee.Classifier.smileRandomForest(50).train({ // Classify the image. var classified = stackedImage.classify(classifier); -Map.addLayer(classified, {min: 0, max: 1, palette: ['white', 'red']}, 'change'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 1, palette: ['white', 'red']}, 'change'); diff --git a/code/end_to_end_gee/04-Change-Detection/04a_Post_Classification_Comparison b/code/end_to_end_gee/04-Change-Detection/04a_Post_Classification_Comparison index 0d240883..3c29b017 100644 --- a/code/end_to_end_gee/04-Change-Detection/04a_Post_Classification_Comparison +++ b/code/end_to_end_gee/04-Change-Detection/04a_Post_Classification_Comparison @@ -249,10 +249,9 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); - -var before = filtered.median().clip(geometry); +var before = filtered.median(); // Display the input composite. -Map.addLayer(before, rgbVis, 'before'); +Map.addLayer(before.clip(geometry), rgbVis, 'before'); var training = urban.merge(bare).merge(water).merge(vegetation); @@ -274,7 +273,7 @@ var classifier = ee.Classifier.smileRandomForest(50).train({ var beforeClassified = before.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; var classifiedVis = {min: 0, max: 3, palette: palette}; -Map.addLayer(beforeClassified, classifiedVis, 'before_classified'); +Map.addLayer(beforeClassified.clip(geometry), classifiedVis, 'before_classified'); // 2020 Jan @@ -282,13 +281,12 @@ var after = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(geometry)) .select('B.*') - .median() - .clip(geometry); - + .median(); + Map.addLayer(after, rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); -Map.addLayer(afterClassified, classifiedVis, 'after_classified'); +Map.addLayer(afterClassified.clip(geometry), classifiedVis, 'after_classified'); diff --git a/code/end_to_end_gee/04-Change-Detection/04b_Post_Classification_Comparison_(complete) b/code/end_to_end_gee/04-Change-Detection/04b_Post_Classification_Comparison_(complete) index 17df822f..b10fefe9 100644 --- a/code/end_to_end_gee/04-Change-Detection/04b_Post_Classification_Comparison_(complete) +++ b/code/end_to_end_gee/04-Change-Detection/04b_Post_Classification_Comparison_(complete) @@ -23,7 +23,7 @@ var filtered = s2 var before = filtered.median().clip(geometry); // Display the input composite. -Map.addLayer(before, rgbVis, 'before'); +Map.addLayer(before.clip(geometry), rgbVis, 'before'); var training = urban.merge(bare).merge(water).merge(vegetation); @@ -45,21 +45,20 @@ var classifier = ee.Classifier.smileRandomForest(50).train({ var beforeClassified = before.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; var classifiedVis = {min: 0, max: 3, palette: palette}; -Map.addLayer(beforeClassified, classifiedVis, 'before_classified'); +Map.addLayer(beforeClassified.clip(geometry), classifiedVis, 'before_classified'); // 2020 Jan var after = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(geometry)) .select('B.*') - .median() - .clip(geometry); + .median(); -Map.addLayer(after, rgbVis, 'after'); +Map.addLayer(after.clip(geometry), rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); -Map.addLayer(afterClassified, classifiedVis, 'after_classified'); +Map.addLayer(afterClassified.clip(geometry), classifiedVis, 'after_classified'); // Reclassify from 0-3 to 1-4 @@ -68,7 +67,7 @@ var afterClasses = afterClassified.remap([0, 1, 2, 3], [1, 2, 3, 4]); // Show all changed areas var changed = afterClasses.subtract(beforeClasses).neq(0); -Map.addLayer(changed, {min:0, max:1, palette: ['white', 'red']}, 'Change'); +Map.addLayer(changed.clip(geometry), {min:0, max:1, palette: ['white', 'red']}, 'Change'); // We multiply the before image with 100 and add the after image // The resulting pixel values will be unique and will represent each unique transition diff --git a/code/end_to_end_gee/04-Change-Detection/04c_Post_Classification_Comparison_(exercise) b/code/end_to_end_gee/04-Change-Detection/04c_Post_Classification_Comparison_(exercise) index 7fd46931..1510b37f 100644 --- a/code/end_to_end_gee/04-Change-Detection/04c_Post_Classification_Comparison_(exercise) +++ b/code/end_to_end_gee/04-Change-Detection/04c_Post_Classification_Comparison_(exercise) @@ -22,9 +22,9 @@ var filtered = s2 .select('B.*'); -var before = filtered.median().clip(geometry); +var before = filtered.median(); // Display the input composite. -Map.addLayer(before, rgbVis, 'before'); +Map.addLayer(before.clip(geometry), rgbVis, 'before'); var training = urban.merge(bare).merge(water).merge(vegetation); @@ -47,7 +47,7 @@ var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; var classifiedVis = {min: 0, max: 3, palette: palette}; var beforeClassified= before.classify(classifier); -Map.addLayer(beforeClassified, classifiedVis, 'before_classified'); +Map.addLayer(beforeClassified.clip(geometry), classifiedVis, 'before_classified'); // 2020 Jan @@ -56,14 +56,13 @@ var after = s2 .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30)) .filter(ee.Filter.bounds(geometry)) .select('B.*') - .median() - .clip(geometry); - + .median(); + Map.addLayer(after, rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); -Map.addLayer(afterClassified, classifiedVis, 'after_classified'); +Map.addLayer(afterClassified.clip(geometry), classifiedVis, 'after_classified'); // Reclassify from 0-3 to 1-4 var beforeClasses = beforeClassified.remap([0, 1, 2, 3], [1, 2, 3, 4]); diff --git a/code/end_to_end_gee/05-Earth-Engine-Apps/05a_Split_Panel_App b/code/end_to_end_gee/05-Earth-Engine-Apps/05a_Split_Panel_App index b49e090f..b70e07dd 100644 --- a/code/end_to_end_gee/05-Earth-Engine-Apps/05a_Split_Panel_App +++ b/code/end_to_end_gee/05-Earth-Engine-Apps/05a_Split_Panel_App @@ -5,7 +5,7 @@ var selected = admin2 var geometry = selected.geometry(); Map.centerObject(geometry) -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED"); // Write a function for Cloud masking var maskS2clouds = function(image) { diff --git a/code/end_to_end_gee/05-Earth-Engine-Apps/05b_Split_Panel_App_(complete) b/code/end_to_end_gee/05-Earth-Engine-Apps/05b_Split_Panel_App_(complete) index f67a5178..7a60cb82 100644 --- a/code/end_to_end_gee/05-Earth-Engine-Apps/05b_Split_Panel_App_(complete) +++ b/code/end_to_end_gee/05-Earth-Engine-Apps/05b_Split_Panel_App_(complete) @@ -5,7 +5,7 @@ var selected = admin2 var geometry = selected.geometry(); Map.centerObject(geometry) -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED"); // Write a function for Cloud masking var maskS2clouds = function(image) { diff --git a/code/end_to_end_gee/05-Earth-Engine-Apps/05c_Split_Panel_App_(exercise) b/code/end_to_end_gee/05-Earth-Engine-Apps/05c_Split_Panel_App_(exercise) index 5754973a..9a93bd0c 100644 --- a/code/end_to_end_gee/05-Earth-Engine-Apps/05c_Split_Panel_App_(exercise) +++ b/code/end_to_end_gee/05-Earth-Engine-Apps/05c_Split_Panel_App_(exercise) @@ -5,7 +5,7 @@ var selected = admin2 var geometry = selected.geometry(); Map.centerObject(geometry) -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED"); // Write a function for Cloud masking var maskS2clouds = function(image) { diff --git a/code/end_to_end_gee/Supplement/Image_Collections/Filter_by_Cloud_Cover_In_Region b/code/end_to_end_gee/Supplement/Image_Collections/Filter_by_Cloud_Cover_In_Region new file mode 100644 index 00000000..72759536 --- /dev/null +++ b/code/end_to_end_gee/Supplement/Image_Collections/Filter_by_Cloud_Cover_In_Region @@ -0,0 +1,75 @@ +var geometry = ee.Geometry.Polygon([[ + [77.4783, 13.0848], + [77.4783, 12.8198], + [77.7502, 12.8198], + [77.7502, 13.0848]] +]); + +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED'); + +Map.addLayer(geometry, {color: 'red'}, 'Selected Region'); +Map.centerObject(geometry); + +var filtered = s2 + .filter(ee.Filter.date('2019-01-01', '2020-01-01')) + .filter(ee.Filter.bounds(geometry)); + +// Write a function for Cloud masking +function maskS2clouds(image) { + var qa = image.select('QA60'); + var cloudBitMask = 1 << 10; + var cirrusBitMask = 1 << 11; + var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and( + qa.bitwiseAnd(cirrusBitMask).eq(0)); + return image.updateMask(mask) + .select('B.*') + .copyProperties(image, ['system:time_start']); +} + +print('Total images', filtered.size()); + +// Add a function that adds a property to each image +// with the cloud cover in the chosen geometry +var calculateCloudCover = function(image) { + // Apply the cloud mask function + var maskedImage = ee.Image(maskS2clouds(image)); + // The image now has some pixels that are masked + // We count the number of unmasked pixels + // Select any band, since all bands have the same mask + // Working with a single band maes the analysis simpler + var bandName = 'B1'; + var band = maskedImage.select(bandName); + var withMaskStats = band.reduceRegion({ + reducer: ee.Reducer.count(), + geometry: geometry, + scale: 10 + }); + var cloudFreePixels = withMaskStats.getNumber(bandName); + + // Remove the mask and count all pixels + var withoutMaskStats = band.unmask(0).reduceRegion({ + reducer: ee.Reducer.count(), + geometry: geometry, + scale: 10 + }); + + var totalPixels = withoutMaskStats.getNumber('B1'); + + var cloudCoverPercentage = ee.Number.expression( + '100*(totalPixels-cloudFreePixels)/totalPixels', { + totalPixels: totalPixels, + cloudFreePixels: cloudFreePixels + }); + return image.set({ + 'CLOUDY_PIXEL_PERCENTAGE_REGION': cloudCoverPercentage + }); +}; + +var filteredWithCount = filtered.map(calculateCloudCover); + +print(filteredWithCount.first()); + +// Filter using the newly created property +var cloudFreeImages = filteredWithCount + .filter(ee.Filter.eq('CLOUDY_PIXEL_PERCENTAGE_REGION', 0)); +print('Cloud Free Images in Region', cloudFreeImages.size()); diff --git a/docs/code/end_to_end_gee/02-Earth-Engine-Intermediate/03b_Computation_on_Image_Collections_(complete) b/docs/code/end_to_end_gee/02-Earth-Engine-Intermediate/03b_Computation_on_Image_Collections_(complete) index 14d6d484..f838863f 100644 --- a/docs/code/end_to_end_gee/02-Earth-Engine-Intermediate/03b_Computation_on_Image_Collections_(complete) +++ b/docs/code/end_to_end_gee/02-Earth-Engine-Intermediate/03b_Computation_on_Image_Collections_(complete) @@ -24,7 +24,7 @@ var withNdvi = filtered.map(addNDVI); var composite = withNdvi.median(); -var ndviComposite = composite.select('ndvi').clip(geometry); +var ndviComposite = composite.select('ndvi'); var palette = [ 'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', @@ -32,4 +32,4 @@ var palette = [ '004C00', '023B01', '012E01', '011D01', '011301']; var ndviVis = {min:0, max:0.5, palette: palette }; -Map.addLayer(ndviComposite, ndviVis, 'ndvi'); +Map.addLayer(ndviComposite.clip(geometry), ndviVis, 'ndvi'); diff --git a/docs/code/end_to_end_gee/02-Earth-Engine-Intermediate/03c_Computation_on_Image_Collections_(exercise) b/docs/code/end_to_end_gee/02-Earth-Engine-Intermediate/03c_Computation_on_Image_Collections_(exercise) index 740b924c..28911c1a 100644 --- a/docs/code/end_to_end_gee/02-Earth-Engine-Intermediate/03c_Computation_on_Image_Collections_(exercise) +++ b/docs/code/end_to_end_gee/02-Earth-Engine-Intermediate/03c_Computation_on_Image_Collections_(exercise) @@ -25,10 +25,11 @@ function addIndices(image) { var withIndices = filtered.map(addIndices); // Composite -var composite = withIndices.median().clip(geometry); +var composite = withIndices.median(); print(composite); -// Extract the 'ndwi' band and display a NDWI map +// Extract the 'ndwi' band +// Clip and display a NDWI map // use the palette ['white', 'blue'] // Hint: Use .select() function to select a band diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/01b_Basic_Supervised_Classification_(complete) b/docs/code/end_to_end_gee/03-Supervised-Classification/01b_Basic_Supervised_Classification_(complete) index e4f706de..bd26c407 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/01b_Basic_Supervised_Classification_(complete) +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/01b_Basic_Supervised_Classification_(complete) @@ -229,10 +229,10 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); var gcps = urban.merge(bare).merge(water).merge(vegetation); @@ -258,6 +258,6 @@ Map.centerObject(geometry); // Urban, Bare, Water, Vegetation var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/01c_Basic_Supervised_Classification_(exercise) b/docs/code/end_to_end_gee/03-Supervised-Classification/01c_Basic_Supervised_Classification_(exercise) index a80aabf7..f1afdb62 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/01c_Basic_Supervised_Classification_(exercise) +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/01c_Basic_Supervised_Classification_(exercise) @@ -16,12 +16,12 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. var rgbVis = {min: 0.0, max: 3000, bands: ['B4', 'B3', 'B2']}; -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Exercise // Add training points for 4 classes @@ -60,6 +60,6 @@ Map.addLayer(composite, rgbVis, 'image'); // // Urban, Bare, Water, Vegetation // var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -// Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +// Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/01d_Basic_Supervised_Classification_(noimport) b/docs/code/end_to_end_gee/03-Supervised-Classification/01d_Basic_Supervised_Classification_(noimport) index 625e958f..a0399a8e 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/01d_Basic_Supervised_Classification_(noimport) +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/01d_Basic_Supervised_Classification_(noimport) @@ -15,7 +15,7 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. var rgbVis = { @@ -23,7 +23,7 @@ var rgbVis = { max: 3000, bands: ['B4', 'B3', 'B2'], }; -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); var gcps = urban.merge(bare).merge(water).merge(vegetation); @@ -48,7 +48,7 @@ var classified = composite.classify(classifier); // Urban, Bare, Water, Vegetation var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); // Display the GCPs // We use the style() function to style the GCPs var palette = ee.List(palette); diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/02a_Accuracy_Assessment b/docs/code/end_to_end_gee/03-Supervised-Classification/02a_Accuracy_Assessment index 3ee38ce6..65cfcaee 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/02a_Accuracy_Assessment +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/02a_Accuracy_Assessment @@ -19,10 +19,10 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Overlay the point on the image to get training data. @@ -44,4 +44,4 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); \ No newline at end of file +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); \ No newline at end of file diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/02b_Accuracy_Assessment_(complete) b/docs/code/end_to_end_gee/03-Supervised-Classification/02b_Accuracy_Assessment_(complete) index 6e35c908..49790ef2 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/02b_Accuracy_Assessment_(complete) +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/02b_Accuracy_Assessment_(complete) @@ -18,10 +18,10 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Add a random column and split the GCPs into training and validation set @@ -53,7 +53,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment //************************************************************************** diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/03a_Improving_the_Classification b/docs/code/end_to_end_gee/03-Supervised-Classification/03a_Improving_the_Classification index 793be4af..37c8b838 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/03a_Improving_the_Classification +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/03a_Improving_the_Classification @@ -20,10 +20,10 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry) +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Add a random column and split the GCPs into training and validation set @@ -55,7 +55,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/03b_Improving_the_Classification_(complete) b/docs/code/end_to_end_gee/03-Supervised-Classification/03b_Improving_the_Classification_(complete) index 29b3ba71..cec7e292 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/03b_Improving_the_Classification_(complete) +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/03b_Improving_the_Classification_(complete) @@ -35,7 +35,7 @@ var filtered = s2 .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); var addIndices = function(image) { @@ -62,7 +62,7 @@ var slope = ee.Terrain.slope(alos.select('AVE_DSM')).rename('slope'); var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -130,7 +130,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/04a_Exporting_Classification_Results b/docs/code/end_to_end_gee/03-Supervised-Classification/04a_Exporting_Classification_Results index d9a39441..7c6cdce0 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/04a_Exporting_Classification_Results +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/04a_Exporting_Classification_Results @@ -34,7 +34,7 @@ var filtered = s2 .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); var addIndices = function(image) { var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']); @@ -60,7 +60,7 @@ var slope = ee.Terrain.slope(alos.select('AVE_DSM')).rename('slope'); var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -129,7 +129,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment //************************************************************************** diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/04b_Exporting_Classification_Results_(complete) b/docs/code/end_to_end_gee/03-Supervised-Classification/04b_Exporting_Classification_Results_(complete) index 995ebdac..a62298de 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/04b_Exporting_Classification_Results_(complete) +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/04b_Exporting_Classification_Results_(complete) @@ -34,7 +34,7 @@ var filtered = s2 .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); var addIndices = function(image) { var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']); @@ -60,7 +60,7 @@ var slope = ee.Terrain.slope(alos.select('AVE_DSM')).rename('slope'); var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -129,7 +129,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment diff --git a/docs/code/end_to_end_gee/03-Supervised-Classification/04c_Exporting_Classification_Results_(exercise) b/docs/code/end_to_end_gee/03-Supervised-Classification/04c_Exporting_Classification_Results_(exercise) index 199b0c62..37fa0767 100644 --- a/docs/code/end_to_end_gee/03-Supervised-Classification/04c_Exporting_Classification_Results_(exercise) +++ b/docs/code/end_to_end_gee/03-Supervised-Classification/04c_Exporting_Classification_Results_(exercise) @@ -34,7 +34,7 @@ var filtered = s2 .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); var addIndices = function(image) { var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']); @@ -60,7 +60,7 @@ var slope = ee.Terrain.slope(alos.select('AVE_DSM')).rename('slope'); var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -129,7 +129,7 @@ var classifier = ee.Classifier.smileRandomForest(50) var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); // Exercise // Use the Export.image.toAsset() function to export the diff --git a/docs/code/end_to_end_gee/04-Change-Detection/03a_Classifying_Change b/docs/code/end_to_end_gee/04-Change-Detection/03a_Classifying_Change index 96879db5..b28a249e 100644 --- a/docs/code/end_to_end_gee/04-Change-Detection/03a_Classifying_Change +++ b/docs/code/end_to_end_gee/04-Change-Detection/03a_Classifying_Change @@ -188,15 +188,15 @@ var filtered = s2 .map(maskS2clouds); -var image2019 = filtered.median().clip(geometry); +var image2019 = filtered.median(); // Display the input composite. -Map.addLayer(image2019, rgbVis, '2019'); +Map.addLayer(image2019.clip(geometry), rgbVis, '2019'); var filtered = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(bangalore)) .map(maskS2clouds) -var image2020 = filtered.median().clip(geometry); +var image2020 = filtered.median(); -Map.addLayer(image2020, rgbVis, '2020'); +Map.addLayer(image2020.clip(geometry), rgbVis, '2020'); diff --git a/docs/code/end_to_end_gee/04-Change-Detection/03b_Classifying_Change_(complete) b/docs/code/end_to_end_gee/04-Change-Detection/03b_Classifying_Change_(complete) index 3112faea..6fd62eaa 100644 --- a/docs/code/end_to_end_gee/04-Change-Detection/03b_Classifying_Change_(complete) +++ b/docs/code/end_to_end_gee/04-Change-Detection/03b_Classifying_Change_(complete) @@ -29,18 +29,18 @@ var filtered = s2 .map(maskS2clouds); -var image2019 = filtered.median().clip(geometry); +var image2019 = filtered.median(); // Display the input composite. -Map.addLayer(image2019, rgbVis, '2019'); +Map.addLayer(image2019.clip(geometry), rgbVis, '2019'); var filtered = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(bangalore)) .map(maskS2clouds); -var image2020 = filtered.median().clip(geometry); +var image2020 = filtered.median(); -Map.addLayer(image2020, rgbVis, '2020'); +Map.addLayer(image2020.clip(geometry), rgbVis, '2020'); var stackedImage = image2019.addBands(image2020); @@ -60,4 +60,4 @@ var classifier = ee.Classifier.smileRandomForest(50).train({ // Classify the image. var classified = stackedImage.classify(classifier); -Map.addLayer(classified, {min: 0, max: 1, palette: ['white', 'red']}, 'change'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 1, palette: ['white', 'red']}, 'change'); diff --git a/docs/code/end_to_end_gee/04-Change-Detection/03c_Classifying_Change_(exercise) b/docs/code/end_to_end_gee/04-Change-Detection/03c_Classifying_Change_(exercise) index 695d48b4..1fb6a974 100644 --- a/docs/code/end_to_end_gee/04-Change-Detection/03c_Classifying_Change_(exercise) +++ b/docs/code/end_to_end_gee/04-Change-Detection/03c_Classifying_Change_(exercise) @@ -30,18 +30,18 @@ var filtered = s2 .map(maskS2clouds); -var image2019 = filtered.median().clip(geometry); +var image2019 = filtered.median(); // Display the input composite. -Map.addLayer(image2019, rgbVis, '2019'); +Map.addLayer(image2019.clip(geometry), rgbVis, '2019'); var filtered = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(bangalore)) .map(maskS2clouds); -var image2020 = filtered.median().clip(geometry); +var image2020 = filtered.median(); -Map.addLayer(image2020, rgbVis, '2020'); +Map.addLayer(image2020.clip(geometry), rgbVis, '2020'); // Exercise @@ -74,4 +74,4 @@ var classifier = ee.Classifier.smileRandomForest(50).train({ // Classify the image. var classified = stackedImage.classify(classifier); -Map.addLayer(classified, {min: 0, max: 1, palette: ['white', 'red']}, 'change'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 1, palette: ['white', 'red']}, 'change'); diff --git a/docs/code/end_to_end_gee/04-Change-Detection/04a_Post_Classification_Comparison b/docs/code/end_to_end_gee/04-Change-Detection/04a_Post_Classification_Comparison index 0d240883..3c29b017 100644 --- a/docs/code/end_to_end_gee/04-Change-Detection/04a_Post_Classification_Comparison +++ b/docs/code/end_to_end_gee/04-Change-Detection/04a_Post_Classification_Comparison @@ -249,10 +249,9 @@ var filtered = s2 .filter(ee.Filter.bounds(geometry)) .select('B.*'); - -var before = filtered.median().clip(geometry); +var before = filtered.median(); // Display the input composite. -Map.addLayer(before, rgbVis, 'before'); +Map.addLayer(before.clip(geometry), rgbVis, 'before'); var training = urban.merge(bare).merge(water).merge(vegetation); @@ -274,7 +273,7 @@ var classifier = ee.Classifier.smileRandomForest(50).train({ var beforeClassified = before.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; var classifiedVis = {min: 0, max: 3, palette: palette}; -Map.addLayer(beforeClassified, classifiedVis, 'before_classified'); +Map.addLayer(beforeClassified.clip(geometry), classifiedVis, 'before_classified'); // 2020 Jan @@ -282,13 +281,12 @@ var after = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(geometry)) .select('B.*') - .median() - .clip(geometry); - + .median(); + Map.addLayer(after, rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); -Map.addLayer(afterClassified, classifiedVis, 'after_classified'); +Map.addLayer(afterClassified.clip(geometry), classifiedVis, 'after_classified'); diff --git a/docs/code/end_to_end_gee/04-Change-Detection/04b_Post_Classification_Comparison_(complete) b/docs/code/end_to_end_gee/04-Change-Detection/04b_Post_Classification_Comparison_(complete) index 17df822f..b10fefe9 100644 --- a/docs/code/end_to_end_gee/04-Change-Detection/04b_Post_Classification_Comparison_(complete) +++ b/docs/code/end_to_end_gee/04-Change-Detection/04b_Post_Classification_Comparison_(complete) @@ -23,7 +23,7 @@ var filtered = s2 var before = filtered.median().clip(geometry); // Display the input composite. -Map.addLayer(before, rgbVis, 'before'); +Map.addLayer(before.clip(geometry), rgbVis, 'before'); var training = urban.merge(bare).merge(water).merge(vegetation); @@ -45,21 +45,20 @@ var classifier = ee.Classifier.smileRandomForest(50).train({ var beforeClassified = before.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; var classifiedVis = {min: 0, max: 3, palette: palette}; -Map.addLayer(beforeClassified, classifiedVis, 'before_classified'); +Map.addLayer(beforeClassified.clip(geometry), classifiedVis, 'before_classified'); // 2020 Jan var after = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(geometry)) .select('B.*') - .median() - .clip(geometry); + .median(); -Map.addLayer(after, rgbVis, 'after'); +Map.addLayer(after.clip(geometry), rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); -Map.addLayer(afterClassified, classifiedVis, 'after_classified'); +Map.addLayer(afterClassified.clip(geometry), classifiedVis, 'after_classified'); // Reclassify from 0-3 to 1-4 @@ -68,7 +67,7 @@ var afterClasses = afterClassified.remap([0, 1, 2, 3], [1, 2, 3, 4]); // Show all changed areas var changed = afterClasses.subtract(beforeClasses).neq(0); -Map.addLayer(changed, {min:0, max:1, palette: ['white', 'red']}, 'Change'); +Map.addLayer(changed.clip(geometry), {min:0, max:1, palette: ['white', 'red']}, 'Change'); // We multiply the before image with 100 and add the after image // The resulting pixel values will be unique and will represent each unique transition diff --git a/docs/code/end_to_end_gee/04-Change-Detection/04c_Post_Classification_Comparison_(exercise) b/docs/code/end_to_end_gee/04-Change-Detection/04c_Post_Classification_Comparison_(exercise) index 7fd46931..1510b37f 100644 --- a/docs/code/end_to_end_gee/04-Change-Detection/04c_Post_Classification_Comparison_(exercise) +++ b/docs/code/end_to_end_gee/04-Change-Detection/04c_Post_Classification_Comparison_(exercise) @@ -22,9 +22,9 @@ var filtered = s2 .select('B.*'); -var before = filtered.median().clip(geometry); +var before = filtered.median(); // Display the input composite. -Map.addLayer(before, rgbVis, 'before'); +Map.addLayer(before.clip(geometry), rgbVis, 'before'); var training = urban.merge(bare).merge(water).merge(vegetation); @@ -47,7 +47,7 @@ var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; var classifiedVis = {min: 0, max: 3, palette: palette}; var beforeClassified= before.classify(classifier); -Map.addLayer(beforeClassified, classifiedVis, 'before_classified'); +Map.addLayer(beforeClassified.clip(geometry), classifiedVis, 'before_classified'); // 2020 Jan @@ -56,14 +56,13 @@ var after = s2 .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30)) .filter(ee.Filter.bounds(geometry)) .select('B.*') - .median() - .clip(geometry); - + .median(); + Map.addLayer(after, rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); -Map.addLayer(afterClassified, classifiedVis, 'after_classified'); +Map.addLayer(afterClassified.clip(geometry), classifiedVis, 'after_classified'); // Reclassify from 0-3 to 1-4 var beforeClasses = beforeClassified.remap([0, 1, 2, 3], [1, 2, 3, 4]); diff --git a/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05a_Split_Panel_App b/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05a_Split_Panel_App index b49e090f..b70e07dd 100644 --- a/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05a_Split_Panel_App +++ b/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05a_Split_Panel_App @@ -5,7 +5,7 @@ var selected = admin2 var geometry = selected.geometry(); Map.centerObject(geometry) -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED"); // Write a function for Cloud masking var maskS2clouds = function(image) { diff --git a/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05b_Split_Panel_App_(complete) b/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05b_Split_Panel_App_(complete) index f67a5178..7a60cb82 100644 --- a/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05b_Split_Panel_App_(complete) +++ b/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05b_Split_Panel_App_(complete) @@ -5,7 +5,7 @@ var selected = admin2 var geometry = selected.geometry(); Map.centerObject(geometry) -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED"); // Write a function for Cloud masking var maskS2clouds = function(image) { diff --git a/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05c_Split_Panel_App_(exercise) b/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05c_Split_Panel_App_(exercise) index 5754973a..9a93bd0c 100644 --- a/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05c_Split_Panel_App_(exercise) +++ b/docs/code/end_to_end_gee/05-Earth-Engine-Apps/05c_Split_Panel_App_(exercise) @@ -5,7 +5,7 @@ var selected = admin2 var geometry = selected.geometry(); Map.centerObject(geometry) -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED"); // Write a function for Cloud masking var maskS2clouds = function(image) { diff --git a/docs/code/end_to_end_gee/Supplement/Image_Collections/Filter_by_Cloud_Cover_In_Region b/docs/code/end_to_end_gee/Supplement/Image_Collections/Filter_by_Cloud_Cover_In_Region new file mode 100644 index 00000000..72759536 --- /dev/null +++ b/docs/code/end_to_end_gee/Supplement/Image_Collections/Filter_by_Cloud_Cover_In_Region @@ -0,0 +1,75 @@ +var geometry = ee.Geometry.Polygon([[ + [77.4783, 13.0848], + [77.4783, 12.8198], + [77.7502, 12.8198], + [77.7502, 13.0848]] +]); + +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED'); + +Map.addLayer(geometry, {color: 'red'}, 'Selected Region'); +Map.centerObject(geometry); + +var filtered = s2 + .filter(ee.Filter.date('2019-01-01', '2020-01-01')) + .filter(ee.Filter.bounds(geometry)); + +// Write a function for Cloud masking +function maskS2clouds(image) { + var qa = image.select('QA60'); + var cloudBitMask = 1 << 10; + var cirrusBitMask = 1 << 11; + var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and( + qa.bitwiseAnd(cirrusBitMask).eq(0)); + return image.updateMask(mask) + .select('B.*') + .copyProperties(image, ['system:time_start']); +} + +print('Total images', filtered.size()); + +// Add a function that adds a property to each image +// with the cloud cover in the chosen geometry +var calculateCloudCover = function(image) { + // Apply the cloud mask function + var maskedImage = ee.Image(maskS2clouds(image)); + // The image now has some pixels that are masked + // We count the number of unmasked pixels + // Select any band, since all bands have the same mask + // Working with a single band maes the analysis simpler + var bandName = 'B1'; + var band = maskedImage.select(bandName); + var withMaskStats = band.reduceRegion({ + reducer: ee.Reducer.count(), + geometry: geometry, + scale: 10 + }); + var cloudFreePixels = withMaskStats.getNumber(bandName); + + // Remove the mask and count all pixels + var withoutMaskStats = band.unmask(0).reduceRegion({ + reducer: ee.Reducer.count(), + geometry: geometry, + scale: 10 + }); + + var totalPixels = withoutMaskStats.getNumber('B1'); + + var cloudCoverPercentage = ee.Number.expression( + '100*(totalPixels-cloudFreePixels)/totalPixels', { + totalPixels: totalPixels, + cloudFreePixels: cloudFreePixels + }); + return image.set({ + 'CLOUDY_PIXEL_PERCENTAGE_REGION': cloudCoverPercentage + }); +}; + +var filteredWithCount = filtered.map(calculateCloudCover); + +print(filteredWithCount.first()); + +// Filter using the newly created property +var cloudFreeImages = filteredWithCount + .filter(ee.Filter.eq('CLOUDY_PIXEL_PERCENTAGE_REGION', 0)); +print('Cloud Free Images in Region', cloudFreeImages.size()); diff --git a/docs/end-to-end-gee-supplement.html b/docs/end-to-end-gee-supplement.html index 8a9f9615..3707afa8 100644 --- a/docs/end-to-end-gee-supplement.html +++ b/docs/end-to-end-gee-supplement.html @@ -427,6 +427,9 @@

Ujaval Gandhi

  • Get Pixelwise Dates for Composites
  • +
  • Filter Images by Cloud +Cover in a Region
  • Harmonized Landsat Time Series
  • @@ -3016,283 +3019,372 @@

    Get Pixelwise Dates for Composites

    -
    -

    Harmonized Landsat Time Series

    +
    +

    Filter Images by Cloud Cover in a Region

    +

    This script shows how to calculate the cloud cover in a region, and +set an image property with the cloud cover for a given region. We can +then apply a filter to select images having no cloud cover in the +region. This is useful where you are working in a very cloudy region and +want to ensure that you are filtering for clouds in your region of +interest, instead of the whole scene.

    Open in Code Editor ↗

    // Script showing how to obtain a harmonized Landsat Time-Series
    -// using Landsat Collection 2
    -var geometry = ee.Geometry.Polygon([[
    -  [82.60642647743225, 27.16350437805251],
    -  [82.60984897613525, 27.1618529901377],
    -  [82.61088967323303, 27.163695288375266],
    -  [82.60757446289062, 27.16517483230927]
    -]]);
    +class="sourceCode js">var geometry = ee.Geometry.Polygon([[
    +    [77.4783, 13.0848],
    +    [77.4783, 12.8198],
    +    [77.7502, 12.8198],
    +    [77.7502, 13.0848]]
    +]);
    +          
    +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
     
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -// Step 1: Select the Landsat dataset
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -
    -// We use "Landsat Level 2 Collection 2 Tier-1 Scenes"
    -
    -// Collection 2 -->
    -// Landsat Collection 2 algorithm has improved
    -// geometric and radiometric calibration that makes
    -// the collections interoperable.
    -// Learn more at https://www.usgs.gov/landsat-missions/landsat-collection-2
    -
    -// Level 2 -->
    -// This is a surface reflectance product and 
    -// have the highest level of interoperability through time.
    -
    -// Tier 1 -->
    -// Highest quality scenes which are considered suitable
    -// for time-series analysis
    -var L5 = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2');
    -var L7 = ee.ImageCollection('LANDSAT/LE07/C02/T1_L2');
    -var L8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
    -
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -// Step 2: Data Pre-Processing and Cloud Masking
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -
    -// Mapping of band-names to a uniform naming scheme
    -var l5Bands = ['SR_B1','SR_B2','SR_B3','SR_B4','SR_B5','SR_B7'];
    -var l5names = ['blue','green','red','nir','swir1','swir2'];
    -
    -var l7Bands = ['SR_B1','SR_B2','SR_B3','SR_B4','SR_B5','SR_B7'];
    -var l7names = ['blue','green','red','nir','swir1','swir2'];
    -
    -var l8Bands = ['SR_B2','SR_B3','SR_B4','SR_B5','SR_B6','SR_B7'];
    -var l8names = ['blue','green','red','nir','swir1','swir2'];
    -
    -// Cloud masking function for Landsat 4,5 and 7
    -function maskL457sr(image) {
    -  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
    -  var saturationMask = image.select('QA_RADSAT').eq(0);
    -
    -  // Apply the scaling factors to the appropriate bands.
    -  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
    -  var thermalBand = image.select('ST_B6').multiply(0.00341802).add(149.0);
    -
    -  // Replace the original bands with the scaled ones and apply the masks.
    -  return image.addBands(opticalBands, null, true)
    -      .addBands(thermalBand, null, true)
    -      .updateMask(qaMask)
    -      .updateMask(saturationMask)
    -      .copyProperties(image, ['system:time_start']);
    -}
    -
    -// Cloud masking function for Landsat 8
    -function maskL8sr(image) {
    -  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
    -  var saturationMask = image.select('QA_RADSAT').eq(0);
    -
    -  // Apply the scaling factors to the appropriate bands.
    -  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
    -  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
    -
    -  // Replace the original bands with the scaled ones and apply the masks.
    -  return image.addBands(opticalBands, null, true)
    -      .addBands(thermalBands, null, true)
    -      .updateMask(qaMask)
    -      .updateMask(saturationMask)
    -      .copyProperties(image, ['system:time_start']);
    -}
    -
    -// Apply cloud-mask and rename bands
    -var L5 = L5
    -  .map(maskL457sr)
    -  .select(l5Bands,l5names)
    -
    -var L7 = L7
    -  .map(maskL457sr)
    -  .select(l7Bands,l7names)
    -
    -var L8 = L8
    -  .map(maskL8sr)
    -  .select(l8Bands,l8names)
    -
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -// Step 3a: Verify Radiometric Calibration
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -// We plot band values from different satellites during
    -// times when both were operational.
    -
    -// Compare L5 and L7
    -var L5Filtered = L5
    -  .filter(ee.Filter.date('2005-01-01', '2006-01-01'))
    -  .select(['red', 'nir'], ['red_L5', 'nir_L5']);
    -
    -var L7Filtered = L7
    -  .filter(ee.Filter.date('2005-01-01', '2006-01-01'))
    -  .select(['red', 'nir'], ['red_L7', 'nir_L7']);
    -
    -var L5L7merged = L5Filtered.merge(L7Filtered)
    -
    -var chart = ui.Chart.image.series({
    -  imageCollection: L5L7merged,
    -  region: geometry,
    -  reducer: ee.Reducer.mean(),
    -  scale: 30
    -}).setChartType('LineChart')
    -  .setOptions({
    -    title: 'Landsat 5 vs Landsat 7',
    -    interpolateNulls: true,
    -    vAxis: {title: 'Reflectance', viewWindow: {min: 0, max: 0.5}},
    -    hAxis: {title: '', format: 'YYYY-MM'},
    -    lineWidth: 1,
    -    pointSize: 4,
    -    lineDashStyle: [4, 4]
    -  })
    -print(chart);
    -
    -// Compare L7 and L8
    -var L7Filtered = L7
    -  .filter(ee.Filter.date('2016-01-01', '2017-01-01'))
    -  .select(['red', 'nir'], ['red_L7', 'nir_L7']);
    -
    -var L8Filtered = L8
    -  .filter(ee.Filter.date('2016-01-01', '2017-01-01'))
    -  .select(['red', 'nir'], ['red_L8', 'nir_L8']);
    -
    -var L7L8merged = L7Filtered.merge(L8Filtered)
    -
    -var chart = ui.Chart.image.series({
    -  imageCollection: L7L8merged,
    -  region: geometry,
    -  reducer: ee.Reducer.mean(),
    -  scale: 30
    -}).setChartType('LineChart')
    -  .setOptions({
    -    title: 'Landsat 7 vs Landsat 8',
    -    interpolateNulls: true,
    -    vAxis: {title: 'Reflectance', viewWindow: {min: 0, max: 0.5}},
    -    hAxis: {title: '', format: 'YYYY-MM'},
    -    lineWidth: 1,
    -    pointSize: 4,
    -    lineDashStyle: [4, 4]
    -  })
    -print(chart);
    -
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -// Step 3b: Select Date Ranges, Filter and Merge
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -// See the Landsat timeline for date ranges
    -// https://www.usgs.gov/media/images/landsat-missions-timeline
    -
    -// Adjust the range depending on your 
    -// application and location
    -var l5Start = ee.Date.fromYMD(1990, 1, 1);
    -var l5End = ee.Date.fromYMD(1999, 1, 1);
    -
    -var l7Start = ee.Date.fromYMD(1999, 1, 1);
    -var l7End = ee.Date.fromYMD(2014, 1, 1);
    -
    -var l8Start = ee.Date.fromYMD(2014, 1, 1);
    -var l8End = ee.Date.fromYMD(2023, 1, 1);
    -
    -var L5 = L5
    -  .filter(ee.Filter.date(l5Start, l5End))
    -  .filter(ee.Filter.bounds(geometry));
    -
    -var L7 = L7
    -  .filter(ee.Filter.date(l7Start, l7End))
    -  .filter(ee.Filter.bounds(geometry));
    -
    -var L8 = L8
    -  .filter(ee.Filter.date(l8Start, l8End))
    -  .filter(ee.Filter.bounds(geometry));
    -  
    -var merged = L5.merge(L7).merge(L8)
    -
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -// Step 4: Create Annual Composites
    -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -var years = ee.List.sequence(1990, 2023);
    -
    -var compositeImages = years.map(function(year) {
    -  var startDate = ee.Date.fromYMD(year, 1, 1);
    -  var endDate = startDate.advance(1, 'year');
    -  var yearFiltered = merged.filter(ee.Filter.date(startDate, endDate));
    -  var composite = yearFiltered.median();
    -  return composite.set({
    -    'year': year,
    -    'system:time_start': startDate.millis(),
    -    'system:time_end': endDate.millis(),
    -  })
    -});
    -
    -var compositeCol = ee.ImageCollection.fromImages(compositeImages);
    -print('Annual Landsat Composites', compositeCol);
    +Map.addLayer(geometry, {color: 'red'}, 'Selected Region'); +Map.centerObject(geometry); + +var filtered = s2 + .filter(ee.Filter.date('2019-01-01', '2020-01-01')) + .filter(ee.Filter.bounds(geometry)); + +// Write a function for Cloud masking +function maskS2clouds(image) { + var qa = image.select('QA60'); + var cloudBitMask = 1 << 10; + var cirrusBitMask = 1 << 11; + var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and( + qa.bitwiseAnd(cirrusBitMask).eq(0)); + return image.updateMask(mask) + .select('B.*') + .copyProperties(image, ['system:time_start']); +} + +print('Total images', filtered.size()); + +// Add a function that adds a property to each image +// with the cloud cover in the chosen geometry +var calculateCloudCover = function(image) { + // Apply the cloud mask function + var maskedImage = ee.Image(maskS2clouds(image)); + // The image now has some pixels that are masked + // We count the number of unmasked pixels + // Select any band, since all bands have the same mask + // Working with a single band maes the analysis simpler + var bandName = 'B1'; + var band = maskedImage.select(bandName); + var withMaskStats = band.reduceRegion({ + reducer: ee.Reducer.count(), + geometry: geometry, + scale: 10 + }); + var cloudFreePixels = withMaskStats.getNumber(bandName); + + // Remove the mask and count all pixels + var withoutMaskStats = band.unmask(0).reduceRegion({ + reducer: ee.Reducer.count(), + geometry: geometry, + scale: 10 + }); + + var totalPixels = withoutMaskStats.getNumber('B1'); + + var cloudCoverPercentage = ee.Number.expression( + '100*(totalPixels-cloudFreePixels)/totalPixels', { + totalPixels: totalPixels, + cloudFreePixels: cloudFreePixels + }); + return image.set({ + 'CLOUDY_PIXEL_PERCENTAGE_REGION': cloudCoverPercentage + }); +}; + +var filteredWithCount = filtered.map(calculateCloudCover); + +print(filteredWithCount.first()); + +// Filter using the newly created property +var cloudFreeImages = filteredWithCount + .filter(ee.Filter.eq('CLOUDY_PIXEL_PERCENTAGE_REGION', 0)); +print('Cloud Free Images in Region', cloudFreeImages.size());
    -
    -

    Visualize Number of Images in Composites

    +
    +

    Harmonized Landsat Time Series

    Open in Code Editor ↗

    // Exploring Composite Images
    -// Example script showing 
    -// 1. How to visualize the DOY (day-of-year) of each pixel of a composite
    -// 2. How to visualize count of images at each pixel of a composite
    -var admin1 = ee.FeatureCollection('FAO/GAUL_SIMPLIFIED_500m/2015/level1');
    -var karnataka = admin1.filter(ee.Filter.eq('ADM1_NAME', 'Karnataka'));
    -var geometry = karnataka.geometry();
    -
    -var s2 =ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
    -
    -var rgbVis = {
    -  min: 0.0,
    -  max: 3000,
    -  bands: ['B4', 'B3', 'B2'],
    -};
    -
    -
    -var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    -  .filter(ee.Filter.date('2019-01-01', '2020-01-01'))
    -  .filter(ee.Filter.bounds(geometry));
    +class="sourceCode js">// Script showing how to obtain a harmonized Landsat Time-Series
    +// using Landsat Collection 2
    +var geometry = ee.Geometry.Polygon([[
    +  [82.60642647743225, 27.16350437805251],
    +  [82.60984897613525, 27.1618529901377],
    +  [82.61088967323303, 27.163695288375266],
    +  [82.60757446289062, 27.16517483230927]
    +]]);
    +
    +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +// Step 1: Select the Landsat dataset
    +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +// We use "Landsat Level 2 Collection 2 Tier-1 Scenes"
    +
    +// Collection 2 -->
    +// Landsat Collection 2 algorithm has improved
    +// geometric and radiometric calibration that makes
    +// the collections interoperable.
    +// Learn more at https://www.usgs.gov/landsat-missions/landsat-collection-2
     
    -// Add a band to each image indicating the DOY of each image
    -var filteredWithDoyBand = filtered.map(function(image) {
    -  // Create an image with day of the year as value
    -  var date = ee.Date(image.get('system:time_start'));
    -  var day = date.getRelative('day', 'year');
    -  var dayImage = ee.Image.constant(day).rename(['dayofyear']).int().clip(image.geometry());
    -  return image.addBands([dayImage]);
    -});
    -
    -var composite = filteredWithDoyBand.median();
    +// Level 2 -->
    +// This is a surface reflectance product and 
    +// have the highest level of interoperability through time.
    +
    +// Tier 1 -->
    +// Highest quality scenes which are considered suitable
    +// for time-series analysis
    +var L5 = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2');
    +var L7 = ee.ImageCollection('LANDSAT/LE07/C02/T1_L2');
    +var L8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
     
    -Map.centerObject(geometry, 8);
    -Map.addLayer(composite.clip(geometry), rgbVis, '2019 Median Composite')
    -
    +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +// Step 2: Data Pre-Processing and Cloud Masking
    +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     
    -// Visualize which pixels contribute to the composite
    -var dateImage = composite.select('dayofyear').int();
    -
    -var doyVis = {
    -  min:20,
    -  max: 100,
    -  palette: ['d7191c','fdae61','ffffbf','a6d96a','1a9641']
    -}
    -Map.addLayer(dateImage.clip(geometry), doyVis, 'DOY of Each Pixel in Composite');
    +// Mapping of band-names to a uniform naming scheme
    +var l5Bands = ['SR_B1','SR_B2','SR_B3','SR_B4','SR_B5','SR_B7'];
    +var l5names = ['blue','green','red','nir','swir1','swir2'];
    +
    +var l7Bands = ['SR_B1','SR_B2','SR_B3','SR_B4','SR_B5','SR_B7'];
    +var l7names = ['blue','green','red','nir','swir1','swir2'];
    +
    +var l8Bands = ['SR_B2','SR_B3','SR_B4','SR_B5','SR_B6','SR_B7'];
    +var l8names = ['blue','green','red','nir','swir1','swir2'];
     
    -// Visualize the number of images contributing to each pixel 
    -// of the composite
    -// Select a single band and use count()
    -var count = filtered.select(['B4']).count();
    +// Cloud masking function for Landsat 4,5 and 7
    +function maskL457sr(image) {
    +  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
    +  var saturationMask = image.select('QA_RADSAT').eq(0);
     
    -// show image count
    -var countVis = {
    -    min: 50,
    -    max: 100,
    -    palette: ['#fee5d9','#fcae91','#fb6a4a','#de2d26','#a50f15']
    -}
    -Map.addLayer(count.clip(geometry), countVis, 'Number of S2 Scenes')
    + // Apply the scaling factors to the appropriate bands. + var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2); + var thermalBand = image.select('ST_B6').multiply(0.00341802).add(149.0); + + // Replace the original bands with the scaled ones and apply the masks. + return image.addBands(opticalBands, null, true) + .addBands(thermalBand, null, true) + .updateMask(qaMask) + .updateMask(saturationMask) + .copyProperties(image, ['system:time_start']); +} + +// Cloud masking function for Landsat 8 +function maskL8sr(image) { + var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0); + var saturationMask = image.select('QA_RADSAT').eq(0); + + // Apply the scaling factors to the appropriate bands. + var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2); + var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0); + + // Replace the original bands with the scaled ones and apply the masks. + return image.addBands(opticalBands, null, true) + .addBands(thermalBands, null, true) + .updateMask(qaMask) + .updateMask(saturationMask) + .copyProperties(image, ['system:time_start']); +} + +// Apply cloud-mask and rename bands +var L5 = L5 + .map(maskL457sr) + .select(l5Bands,l5names) + +var L7 = L7 + .map(maskL457sr) + .select(l7Bands,l7names) + +var L8 = L8 + .map(maskL8sr) + .select(l8Bands,l8names) + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Step 3a: Verify Radiometric Calibration +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// We plot band values from different satellites during +// times when both were operational. + +// Compare L5 and L7 +var L5Filtered = L5 + .filter(ee.Filter.date('2005-01-01', '2006-01-01')) + .select(['red', 'nir'], ['red_L5', 'nir_L5']); + +var L7Filtered = L7 + .filter(ee.Filter.date('2005-01-01', '2006-01-01')) + .select(['red', 'nir'], ['red_L7', 'nir_L7']); + +var L5L7merged = L5Filtered.merge(L7Filtered) + +var chart = ui.Chart.image.series({ + imageCollection: L5L7merged, + region: geometry, + reducer: ee.Reducer.mean(), + scale: 30 +}).setChartType('LineChart') + .setOptions({ + title: 'Landsat 5 vs Landsat 7', + interpolateNulls: true, + vAxis: {title: 'Reflectance', viewWindow: {min: 0, max: 0.5}}, + hAxis: {title: '', format: 'YYYY-MM'}, + lineWidth: 1, + pointSize: 4, + lineDashStyle: [4, 4] + }) +print(chart); + +// Compare L7 and L8 +var L7Filtered = L7 + .filter(ee.Filter.date('2016-01-01', '2017-01-01')) + .select(['red', 'nir'], ['red_L7', 'nir_L7']); + +var L8Filtered = L8 + .filter(ee.Filter.date('2016-01-01', '2017-01-01')) + .select(['red', 'nir'], ['red_L8', 'nir_L8']); + +var L7L8merged = L7Filtered.merge(L8Filtered) + +var chart = ui.Chart.image.series({ + imageCollection: L7L8merged, + region: geometry, + reducer: ee.Reducer.mean(), + scale: 30 +}).setChartType('LineChart') + .setOptions({ + title: 'Landsat 7 vs Landsat 8', + interpolateNulls: true, + vAxis: {title: 'Reflectance', viewWindow: {min: 0, max: 0.5}}, + hAxis: {title: '', format: 'YYYY-MM'}, + lineWidth: 1, + pointSize: 4, + lineDashStyle: [4, 4] + }) +print(chart); + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Step 3b: Select Date Ranges, Filter and Merge +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// See the Landsat timeline for date ranges +// https://www.usgs.gov/media/images/landsat-missions-timeline + +// Adjust the range depending on your +// application and location +var l5Start = ee.Date.fromYMD(1990, 1, 1); +var l5End = ee.Date.fromYMD(1999, 1, 1); + +var l7Start = ee.Date.fromYMD(1999, 1, 1); +var l7End = ee.Date.fromYMD(2014, 1, 1); + +var l8Start = ee.Date.fromYMD(2014, 1, 1); +var l8End = ee.Date.fromYMD(2023, 1, 1); + +var L5 = L5 + .filter(ee.Filter.date(l5Start, l5End)) + .filter(ee.Filter.bounds(geometry)); + +var L7 = L7 + .filter(ee.Filter.date(l7Start, l7End)) + .filter(ee.Filter.bounds(geometry)); + +var L8 = L8 + .filter(ee.Filter.date(l8Start, l8End)) + .filter(ee.Filter.bounds(geometry)); + +var merged = L5.merge(L7).merge(L8) + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Step 4: Create Annual Composites +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var years = ee.List.sequence(1990, 2023); + +var compositeImages = years.map(function(year) { + var startDate = ee.Date.fromYMD(year, 1, 1); + var endDate = startDate.advance(1, 'year'); + var yearFiltered = merged.filter(ee.Filter.date(startDate, endDate)); + var composite = yearFiltered.median(); + return composite.set({ + 'year': year, + 'system:time_start': startDate.millis(), + 'system:time_end': endDate.millis(), + }) +}); + +var compositeCol = ee.ImageCollection.fromImages(compositeImages); +print('Annual Landsat Composites', compositeCol);
    +
    +
    +

    Visualize Number of Images in Composites

    +

    Open in Code Editor ↗

    +
    // Exploring Composite Images
    +// Example script showing 
    +// 1. How to visualize the DOY (day-of-year) of each pixel of a composite
    +// 2. How to visualize count of images at each pixel of a composite
    +var admin1 = ee.FeatureCollection('FAO/GAUL_SIMPLIFIED_500m/2015/level1');
    +var karnataka = admin1.filter(ee.Filter.eq('ADM1_NAME', 'Karnataka'));
    +var geometry = karnataka.geometry();
    +
    +var s2 =ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
    +
    +var rgbVis = {
    +  min: 0.0,
    +  max: 3000,
    +  bands: ['B4', 'B3', 'B2'],
    +};
    +
    +
    +var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    +  .filter(ee.Filter.date('2019-01-01', '2020-01-01'))
    +  .filter(ee.Filter.bounds(geometry));
    +
    +// Add a band to each image indicating the DOY of each image
    +var filteredWithDoyBand = filtered.map(function(image) {
    +  // Create an image with day of the year as value
    +  var date = ee.Date(image.get('system:time_start'));
    +  var day = date.getRelative('day', 'year');
    +  var dayImage = ee.Image.constant(day).rename(['dayofyear']).int().clip(image.geometry());
    +  return image.addBands([dayImage]);
    +});
    +
    +var composite = filteredWithDoyBand.median();
    +
    +Map.centerObject(geometry, 8);
    +Map.addLayer(composite.clip(geometry), rgbVis, '2019 Median Composite')
    +
    +
    +// Visualize which pixels contribute to the composite
    +var dateImage = composite.select('dayofyear').int();
    +
    +var doyVis = {
    +  min:20,
    +  max: 100,
    +  palette: ['d7191c','fdae61','ffffbf','a6d96a','1a9641']
    +}
    +Map.addLayer(dateImage.clip(geometry), doyVis, 'DOY of Each Pixel in Composite');
    +
    +// Visualize the number of images contributing to each pixel 
    +// of the composite
    +// Select a single band and use count()
    +var count = filtered.select(['B4']).count();
    +
    +// show image count
    +var countVis = {
    +    min: 50,
    +    max: 100,
    +    palette: ['#fee5d9','#fcae91','#fb6a4a','#de2d26','#a50f15']
    +}
    +Map.addLayer(count.clip(geometry), countVis, 'Number of S2 Scenes')
    @@ -3302,76 +3394,76 @@

    Working with Landsat Collection 2

    Open in Code Editor ↗

    -
    // Example script for calculating NDVI and EVI from Landsat Collection 2 images
    -
    -// Get Banglore boundary 
    -var admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2");
    - 
    -var bangalore = admin2.filter(ee.Filter.eq('ADM2_NAME', 'Bangalore Urban'))
    -var geometry = bangalore.geometry()
    -
    -// Applies cloud mask and scaling factors.
    -function maskL8sr(image) {
    -  // Bit 0 - Fill
    -  // Bit 1 - Dilated Cloud
    -  // Bit 2 - Cirrus
    -  // Bit 3 - Cloud
    -  // Bit 4 - Cloud Shadow
    -  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
    -  var saturationMask = image.select('QA_RADSAT').eq(0);
    -
    -  // Apply the scaling factors to the appropriate bands.
    -  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
    -  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
    -
    -  // Replace the original bands with the scaled ones and apply the masks.
    -  return image.addBands(opticalBands, null, true)
    -      .addBands(thermalBands, null, true)
    -      .updateMask(qaMask)
    -      .updateMask(saturationMask);
    -}
    -
    -// Filter to 2021 Landsat 8 images over banglore. 
    -var dataset = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    -    .filter(ee.Filter.date('2021-01-01', '2022-01-01'))
    -    .filter(ee.Filter.bounds(geometry))
    -    .map(maskL8sr);
    -
    -// Create a median composite
    -var image = dataset.median(); 
    -
    -// Print to check the bands. 
    -print(image)
    -
    -// Create NDVI image. 
    -var ndvi = image.normalizedDifference(['SR_B5', 'SR_B4']).rename(['ndvi'])
    -
    -// Create MNDWI image. 
    -var mndwi = image.normalizedDifference(['SR_B3', 'SR_B6']).rename(['mndwi'])
    -
    -// Create EVI image
    -// EVI = 2.5 * ((Band 5 – Band 4) / (Band 5 + 6 * Band 4 – 7.5 * Band 2 + 1)).
    -var evi = image.expression(
    -    '2.5 * ( (NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', {
    -      'BLUE': image.select('SR_B2'),
    -      'RED': image.select('SR_B4'),
    -      'NIR': image.select('SR_B5')
    -}).rename('evi');
    -
    -// Create MNDWI image
    -
    -// Visualization parameter. 
    -var rgbVis = {min:0, max:0.3, bands:['SR_B4', 'SR_B3', 'SR_B2']}
    -var ndviVis = {min:0, max:0.5,  palette: ['white', 'green']}
    -var ndwiVis = {min:0, max:0.5,  palette: ['white', 'blue']}
    -
    -// Add EVI and NDVI images to Map. 
    -Map.centerObject(geometry)
    -Map.addLayer(image.clip(geometry), rgbVis, 'Image')
    -Map.addLayer(evi.clip(geometry), ndviVis, 'EVI')
    -Map.addLayer(ndvi.clip(geometry), ndviVis, 'NDVI')
    -Map.addLayer(mndwi.clip(geometry), ndwiVis, 'MNDWI')
    +
    // Example script for calculating NDVI and EVI from Landsat Collection 2 images
    +
    +// Get Banglore boundary 
    +var admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2");
    + 
    +var bangalore = admin2.filter(ee.Filter.eq('ADM2_NAME', 'Bangalore Urban'))
    +var geometry = bangalore.geometry()
    +
    +// Applies cloud mask and scaling factors.
    +function maskL8sr(image) {
    +  // Bit 0 - Fill
    +  // Bit 1 - Dilated Cloud
    +  // Bit 2 - Cirrus
    +  // Bit 3 - Cloud
    +  // Bit 4 - Cloud Shadow
    +  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
    +  var saturationMask = image.select('QA_RADSAT').eq(0);
    +
    +  // Apply the scaling factors to the appropriate bands.
    +  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
    +  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
    +
    +  // Replace the original bands with the scaled ones and apply the masks.
    +  return image.addBands(opticalBands, null, true)
    +      .addBands(thermalBands, null, true)
    +      .updateMask(qaMask)
    +      .updateMask(saturationMask);
    +}
    +
    +// Filter to 2021 Landsat 8 images over banglore. 
    +var dataset = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    +    .filter(ee.Filter.date('2021-01-01', '2022-01-01'))
    +    .filter(ee.Filter.bounds(geometry))
    +    .map(maskL8sr);
    +
    +// Create a median composite
    +var image = dataset.median(); 
    +
    +// Print to check the bands. 
    +print(image)
    +
    +// Create NDVI image. 
    +var ndvi = image.normalizedDifference(['SR_B5', 'SR_B4']).rename(['ndvi'])
    +
    +// Create MNDWI image. 
    +var mndwi = image.normalizedDifference(['SR_B3', 'SR_B6']).rename(['mndwi'])
    +
    +// Create EVI image
    +// EVI = 2.5 * ((Band 5 – Band 4) / (Band 5 + 6 * Band 4 – 7.5 * Band 2 + 1)).
    +var evi = image.expression(
    +    '2.5 * ( (NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', {
    +      'BLUE': image.select('SR_B2'),
    +      'RED': image.select('SR_B4'),
    +      'NIR': image.select('SR_B5')
    +}).rename('evi');
    +
    +// Create MNDWI image
    +
    +// Visualization parameter. 
    +var rgbVis = {min:0, max:0.3, bands:['SR_B4', 'SR_B3', 'SR_B2']}
    +var ndviVis = {min:0, max:0.5,  palette: ['white', 'green']}
    +var ndwiVis = {min:0, max:0.5,  palette: ['white', 'blue']}
    +
    +// Add EVI and NDVI images to Map. 
    +Map.centerObject(geometry)
    +Map.addLayer(image.clip(geometry), rgbVis, 'Image')
    +Map.addLayer(evi.clip(geometry), ndviVis, 'EVI')
    +Map.addLayer(ndvi.clip(geometry), ndviVis, 'NDVI')
    +Map.addLayer(mndwi.clip(geometry), ndwiVis, 'MNDWI')

    Derive LST from Landsat Images

    @@ -3399,166 +3491,166 @@

    Derive LST from Landsat Images

    Open in Code Editor ↗

    -
    // Script showing how to obtain a Landsat LST Time-Series
    -// over different land surfaces
    -
    -var metalroof = ee.Geometry.Point([72.8550936937685, 19.044646120301234]);
    -var concreteroof =  ee.Geometry.Point([72.85441764267667, 19.028290540890772]);
    -var airport = ee.Geometry.Point([72.86249644714638, 19.09355985643176]);
    -var water = ee.Geometry.Point([72.91107782264197, 19.152799035509638]);
    -var mangrove =  ee.Geometry.Point([72.8115905761819, 19.152316393168405]);
    -    
    -// Use Mumbai city boundary
    -var mumbai_wards = ee.FeatureCollection(
    -  'users/ujavalgandhi/public/mumbai_bmc_wards_datameet');
    -var geometry = mumbai_wards.geometry();
    -Map.centerObject(geometry, 12);
    -
    -// Method 1
    -// LST Computation code by Sofia Ermida (sofia.ermida@ipma.pt; @ermida_sofia)
    -
    -// Ermida, S.L., Soares, P., Mantas, V., Göttsche, F.-M., Trigo, I.F., 2020. 
    -//     Google Earth Engine open-source code for Land Surface Temperature estimation from the Landsat series.
    -//     Remote Sensing, 12 (9), 1471; https://doi.org/10.3390/rs12091471
    -var LandsatLST = require('users/sofiaermida/landsat_smw_lst:modules/Landsat_LST.js')
    -
    -// Set parameters to get Landsat 8 data
    -var satellite = 'L8';
    -var date_start = '2015-01-01';
    -var date_end = '2016-01-01';
    -var use_ndvi = true;
    -
    -// get landsat collection with added variables: NDVI, FVC, TPW, EM, LST
    -var LandsatColl = LandsatLST.collection(satellite, date_start, date_end, geometry, use_ndvi)
    -
    -// Select LST band
    -var lstK = LandsatColl.select('LST')
    -
    -// Convert to celsius
    -var lstC = lstK.map(function(image){
    -  return image.subtract(273.15).copyProperties(image, image.propertyNames())
    -})
    -
    -
    -// Filter to May month image to visualize in map. 
    -var lstMay  = lstC
    -  .filter(ee.Filter.date('2015-04-01', '2015-05-01'))
    -  .mean()
    -
    -Map.addLayer(lstMay.clip(geometry), 
    -  {min:25, max:45, palette:['green','yellow','red']}, 
    -  'Landsat-LST (Ermida, S.L)')
    -
    -// Create the LSt time series chart. 
    -var chart = ui.Chart.image.seriesByRegion({
    -  imageCollection:lstC,  
    -  regions: [airport, metalroof, concreteroof, mangrove, water],
    -  reducer:ee.Reducer.mean(),
    -  band:['LST'],
    -  scale:30,  
    -  xProperty:'system:time_start',
    -}).setOptions({
    -      lineWidth: 1,
    -      title: 'Land Surface Temperature Time-Series (Ermida, S.L)',
    -      interpolateNulls: true,
    -      viewWindowMode:'explicit',
    -        viewWindow: {
    -            max:50,
    -            min:25
    -        },
    -      vAxis: {title: 'LST (°C)'},
    -      hAxis: {title: '', format: 'YYYY-MMM'},
    -      series: {
    -      0: {color: 'red', labelInLegend: 'Airport Tarmac'}, 
    -      1: {color: 'pink', labelInLegend: 'Residential-Slum (Metal Roof)'}, 
    -      2: {color: 'grey', labelInLegend: 'Residential (Concrete Roof)'},
    -      3: {color: 'green', labelInLegend: 'Mangrove'},
    -      4: {color: 'blue', labelInLegend: 'Water'}
    -    } 
    -    })
    -  
    -print(chart);
    -
    -// Method 2
    -// Landsat Collection 2 Level 2 LST
    -
    -var date_start = '2015-01-01';
    -var date_end = '2016-01-01';
    -
    -var dataset = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    -    .filterDate(date_start, date_end)
    -    .filter(ee.Filter.bounds(geometry))
    -    
    -function maskL8sr(image) {
    -  // Bit 0 - Fill
    -  // Bit 1 - Dilated Cloud
    -  // Bit 2 - Cirrus
    -  // Bit 3 - Cloud
    -  // Bit 4 - Cloud Shadow
    -  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
    -  var saturationMask = image.select('QA_RADSAT').eq(0);
    -
    -  // Apply the scaling factors to the appropriate bands.
    -  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
    -  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
    -
    -  // Replace the original bands with the scaled ones and apply the masks.
    -  return image.addBands(opticalBands, null, true)
    -      .addBands(thermalBands, null, true)
    -      .updateMask(qaMask)
    -      .updateMask(saturationMask);
    -}
    -
    -
    -dataset = dataset.map(maskL8sr)
    -
    -// Select B10 band and rename it to LST
    -var lstK = dataset.select(['ST_B10'], ['LST'])
    -
    -// Convert to celsius
    -var lstC = lstK.map(function(image){
    -  return image.subtract(273.15).copyProperties(image, image.propertyNames())
    -})
    -
    -
    -// Filter to May month image to visualize in map. 
    -var lstMay  = lstC
    -  .filter(ee.Filter.date('2015-04-01', '2015-05-01')).mean()
    -
    -Map.addLayer(lstMay.clip(geometry),
    -  {min:25, max:45, palette:['green','yellow','red']},
    -  'Landsat-LST (Landsat Collection 2)')
    -
    -// Create the LSt time series chart. 
    -var chart = ui.Chart.image.seriesByRegion({
    -  imageCollection:lstC,  
    -  regions: [airport, metalroof, concreteroof, mangrove, water],
    -  reducer:ee.Reducer.mean(),
    -  band:['LST'],
    -  scale:30,  
    -  xProperty:'system:time_start',
    -}).setOptions({
    -      lineWidth: 1,
    -      title: 'Land Surface Temperature Time-Series (Landsat Collection 2)',
    -      interpolateNulls: true,
    -      viewWindowMode:'explicit',
    -        viewWindow: {
    -            max:50,
    -            min:25
    -        },
    -      vAxis: {title: 'LST (°C)'},
    -      hAxis: {title: '', format: 'YYYY-MMM'},
    -      series: {
    -      0: {color: 'red', labelInLegend: 'Airport Tarmac'}, 
    -      1: {color: 'pink', labelInLegend: 'Residential-Slum (Metal Roof)'}, 
    -      2: {color: 'grey', labelInLegend: 'Residential (Concrete Roof)'},
    -      3: {color: 'green', labelInLegend: 'Mangrove'},
    -      4: {color: 'blue', labelInLegend: 'Water'}
    -    } 
    -    })
    -  
    -print(chart);
    +
    // Script showing how to obtain a Landsat LST Time-Series
    +// over different land surfaces
    +
    +var metalroof = ee.Geometry.Point([72.8550936937685, 19.044646120301234]);
    +var concreteroof =  ee.Geometry.Point([72.85441764267667, 19.028290540890772]);
    +var airport = ee.Geometry.Point([72.86249644714638, 19.09355985643176]);
    +var water = ee.Geometry.Point([72.91107782264197, 19.152799035509638]);
    +var mangrove =  ee.Geometry.Point([72.8115905761819, 19.152316393168405]);
    +    
    +// Use Mumbai city boundary
    +var mumbai_wards = ee.FeatureCollection(
    +  'users/ujavalgandhi/public/mumbai_bmc_wards_datameet');
    +var geometry = mumbai_wards.geometry();
    +Map.centerObject(geometry, 12);
    +
    +// Method 1
    +// LST Computation code by Sofia Ermida (sofia.ermida@ipma.pt; @ermida_sofia)
    +
    +// Ermida, S.L., Soares, P., Mantas, V., Göttsche, F.-M., Trigo, I.F., 2020. 
    +//     Google Earth Engine open-source code for Land Surface Temperature estimation from the Landsat series.
    +//     Remote Sensing, 12 (9), 1471; https://doi.org/10.3390/rs12091471
    +var LandsatLST = require('users/sofiaermida/landsat_smw_lst:modules/Landsat_LST.js')
    +
    +// Set parameters to get Landsat 8 data
    +var satellite = 'L8';
    +var date_start = '2015-01-01';
    +var date_end = '2016-01-01';
    +var use_ndvi = true;
    +
    +// get landsat collection with added variables: NDVI, FVC, TPW, EM, LST
    +var LandsatColl = LandsatLST.collection(satellite, date_start, date_end, geometry, use_ndvi)
    +
    +// Select LST band
    +var lstK = LandsatColl.select('LST')
    +
    +// Convert to celsius
    +var lstC = lstK.map(function(image){
    +  return image.subtract(273.15).copyProperties(image, image.propertyNames())
    +})
    +
    +
    +// Filter to May month image to visualize in map. 
    +var lstMay  = lstC
    +  .filter(ee.Filter.date('2015-04-01', '2015-05-01'))
    +  .mean()
    +
    +Map.addLayer(lstMay.clip(geometry), 
    +  {min:25, max:45, palette:['green','yellow','red']}, 
    +  'Landsat-LST (Ermida, S.L)')
    +
    +// Create the LSt time series chart. 
    +var chart = ui.Chart.image.seriesByRegion({
    +  imageCollection:lstC,  
    +  regions: [airport, metalroof, concreteroof, mangrove, water],
    +  reducer:ee.Reducer.mean(),
    +  band:['LST'],
    +  scale:30,  
    +  xProperty:'system:time_start',
    +}).setOptions({
    +      lineWidth: 1,
    +      title: 'Land Surface Temperature Time-Series (Ermida, S.L)',
    +      interpolateNulls: true,
    +      viewWindowMode:'explicit',
    +        viewWindow: {
    +            max:50,
    +            min:25
    +        },
    +      vAxis: {title: 'LST (°C)'},
    +      hAxis: {title: '', format: 'YYYY-MMM'},
    +      series: {
    +      0: {color: 'red', labelInLegend: 'Airport Tarmac'}, 
    +      1: {color: 'pink', labelInLegend: 'Residential-Slum (Metal Roof)'}, 
    +      2: {color: 'grey', labelInLegend: 'Residential (Concrete Roof)'},
    +      3: {color: 'green', labelInLegend: 'Mangrove'},
    +      4: {color: 'blue', labelInLegend: 'Water'}
    +    } 
    +    })
    +  
    +print(chart);
    +
    +// Method 2
    +// Landsat Collection 2 Level 2 LST
    +
    +var date_start = '2015-01-01';
    +var date_end = '2016-01-01';
    +
    +var dataset = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    +    .filterDate(date_start, date_end)
    +    .filter(ee.Filter.bounds(geometry))
    +    
    +function maskL8sr(image) {
    +  // Bit 0 - Fill
    +  // Bit 1 - Dilated Cloud
    +  // Bit 2 - Cirrus
    +  // Bit 3 - Cloud
    +  // Bit 4 - Cloud Shadow
    +  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
    +  var saturationMask = image.select('QA_RADSAT').eq(0);
    +
    +  // Apply the scaling factors to the appropriate bands.
    +  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
    +  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
    +
    +  // Replace the original bands with the scaled ones and apply the masks.
    +  return image.addBands(opticalBands, null, true)
    +      .addBands(thermalBands, null, true)
    +      .updateMask(qaMask)
    +      .updateMask(saturationMask);
    +}
    +
    +
    +dataset = dataset.map(maskL8sr)
    +
    +// Select B10 band and rename it to LST
    +var lstK = dataset.select(['ST_B10'], ['LST'])
    +
    +// Convert to celsius
    +var lstC = lstK.map(function(image){
    +  return image.subtract(273.15).copyProperties(image, image.propertyNames())
    +})
    +
    +
    +// Filter to May month image to visualize in map. 
    +var lstMay  = lstC
    +  .filter(ee.Filter.date('2015-04-01', '2015-05-01')).mean()
    +
    +Map.addLayer(lstMay.clip(geometry),
    +  {min:25, max:45, palette:['green','yellow','red']},
    +  'Landsat-LST (Landsat Collection 2)')
    +
    +// Create the LSt time series chart. 
    +var chart = ui.Chart.image.seriesByRegion({
    +  imageCollection:lstC,  
    +  regions: [airport, metalroof, concreteroof, mangrove, water],
    +  reducer:ee.Reducer.mean(),
    +  band:['LST'],
    +  scale:30,  
    +  xProperty:'system:time_start',
    +}).setOptions({
    +      lineWidth: 1,
    +      title: 'Land Surface Temperature Time-Series (Landsat Collection 2)',
    +      interpolateNulls: true,
    +      viewWindowMode:'explicit',
    +        viewWindow: {
    +            max:50,
    +            min:25
    +        },
    +      vAxis: {title: 'LST (°C)'},
    +      hAxis: {title: '', format: 'YYYY-MMM'},
    +      series: {
    +      0: {color: 'red', labelInLegend: 'Airport Tarmac'}, 
    +      1: {color: 'pink', labelInLegend: 'Residential-Slum (Metal Roof)'}, 
    +      2: {color: 'grey', labelInLegend: 'Residential (Concrete Roof)'},
    +      3: {color: 'green', labelInLegend: 'Mangrove'},
    +      4: {color: 'blue', labelInLegend: 'Water'}
    +    } 
    +    })
    +  
    +print(chart);
    @@ -3585,180 +3677,8 @@

    Moving Window Smoothing

    Open in Code Editor ↗

    -
    // Moving-Window Temporal Smoothing 
    -var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
    -var geometry = ee.Geometry.Point([74.80368345518073, 30.391793042969]);
    -
    -var startDate = ee.Date.fromYMD(2019, 1, 1);
    -var endDate = ee.Date.fromYMD(2021, 1, 1);
    -
    -// Function to add a NDVI band to an image
    -function addNDVI(image) {
    -  var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
    -  return image.addBands(ndvi);
    -} 
    -
    -// Function to mask clouds
    -function maskS2clouds(image) {
    -  var qa = image.select('QA60')
    -  var cloudBitMask = 1 << 10;
    -  var cirrusBitMask = 1 << 11;
    -  var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(
    -             qa.bitwiseAnd(cirrusBitMask).eq(0))
    -  return image.updateMask(mask).divide(10000)
    -      .select("B.*")
    -      .copyProperties(image, ["system:time_start"])
    -}
    -
    -var originalCollection = s2
    -  .filter(ee.Filter.date(startDate, endDate))
    -  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    -  .filter(ee.Filter.bounds(geometry))
    -  .map(maskS2clouds)
    -  .map(addNDVI);
    -
    -// Display a time-series chart
    -var chart = ui.Chart.image.series({
    -  imageCollection: originalCollection.select('ndvi'),
    -  region: geometry,
    -  reducer: ee.Reducer.mean(),
    -  scale: 20
    -}).setOptions({
    -      title: 'Original NDVI Time Series',
    -      interpolateNulls: false,
    -      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    -      hAxis: {title: '', format: 'YYYY-MM'},
    -      lineWidth: 1,
    -      pointSize: 4,
    -      series: {
    -        0: {color: '#238b45'},
    -      },
    -
    -    })
    -print(chart);
    -
    -// Moving-Window Smoothing
    -
    -// Specify the time-window
    -var days = 15;
    -
    -// Convert to milliseconds 
    -var millis = ee.Number(days).multiply(1000*60*60*24);
    -
    -// We use a 'save-all join' to find all images 
    -// that are within the time-window
    -
    -// The join will add all matching images into a
    -// new property called 'images'
    -var join = ee.Join.saveAll({
    -  matchesKey: 'images'
    -});
    -
    -// This filter will match all images that are captured
    -// within the specified day of the source image
    -var diffFilter = ee.Filter.maxDifference({
    -  difference: millis,
    -  leftField: 'system:time_start', 
    -  rightField: 'system:time_start'
    -});
    -
    -
    -var joinedCollection = join.apply({
    -  primary: originalCollection, 
    -  secondary: originalCollection, 
    -  condition: diffFilter
    -});
    -
    -print('Joined Collection', joinedCollection);
    -
    -// Each image in the joined collection will contain
    -// matching images in the 'images' property
    -// Extract and return the mean of matched images
    -var extractAndComputeMean = function(image) {
    -  var matchingImages = ee.ImageCollection.fromImages(image.get('images'));
    -  var meanImage = matchingImages.reduce(
    -    ee.Reducer.mean().setOutputs(['moving_average']))
    -  return ee.Image(image).addBands(meanImage)
    -}
    -
    -var smoothedCollection = ee.ImageCollection(
    -  joinedCollection.map(extractAndComputeMean));
    -
    -print('Smoothed Collection', smoothedCollection)
    -
    -// Display a time-series chart
    -var chart = ui.Chart.image.series({
    -  imageCollection: smoothedCollection.select(['ndvi', 'ndvi_moving_average']),
    -  region: geometry,
    -  reducer: ee.Reducer.mean(),
    -  scale: 20
    -}).setOptions({
    -      title: 'NDVI Time Series',
    -      interpolateNulls: false,
    -      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    -      hAxis: {title: '', format: 'YYYY-MM'},
    -      lineWidth: 1,
    -      pointSize: 4,
    -      series: {
    -        0: {color: '#66c2a4', lineDashStyle: [1, 1], pointSize: 2}, // Original NDVI
    -        1: {color: '#238b45', lineWidth: 2 }, // Smoothed NDVI
    -      },
    -
    -    })
    -print(chart);
    -
    -// Let's export the NDVI time-series as a video
    -var palette = ['#d73027','#f46d43','#fdae61','#fee08b',
    -  '#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850'];
    -var ndviVis = {min:-0.2, max: 0.8,  palette: palette}
    -
    -Map.centerObject(geometry, 16);
    -var bbox = Map.getBounds({asGeoJSON: true});
    -
    -var visualizeImage = function(image) {
    -  return image.visualize(ndviVis).clip(bbox).selfMask()
    -}
    -
    -var visCollectionOriginal = originalCollection.select('ndvi')
    -  .map(visualizeImage)
    -
    -
    -var visCollectionSmoothed = smoothedCollection.select('ndvi_moving_average')
    -  .map(visualizeImage)
    -
    -
    -Export.video.toDrive({
    -  collection: visCollectionOriginal,
    -  description: 'Original_Time_Series',
    -  folder: 'earthengine',
    -  fileNamePrefix: 'original',
    -  framesPerSecond: 2,
    -  dimensions: 800,
    -  region: bbox})
    -  
    -Export.video.toDrive({
    -  collection: visCollectionSmoothed,
    -  description: 'Smoothed_Time_Series',
    -  folder: 'earthengine',
    -  fileNamePrefix: 'smoothed',
    -  framesPerSecond: 2,
    -  dimensions: 800,
    -  region: bbox})
    -
    -
    -

    Temporal Interpolation

    -

    The code below shows how to do temporal gap-filling of time-series -data. A detailed explanation of the code and other examples are -described in our blog post Temporal Gap-Filling with Linear Interpolation in -GEE.

    -

    Open in Code Editor ↗

    // Temporal Interpolation (Gap-Filling Masked Pixels)
    +class="sourceCode js">// Moving-Window Temporal Smoothing 
     var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
     var geometry = ee.Geometry.Point([74.80368345518073, 30.391793042969]);
     
    @@ -3790,230 +3710,402 @@ 

    Temporal Interpolation

    .map(maskS2clouds) .map(addNDVI); - -// Display a time-series chart -var chart = ui.Chart.image.series({ - imageCollection: originalCollection.select('ndvi'), - region: geometry, - reducer: ee.Reducer.mean(), - scale: 20 -}).setOptions({ - title: 'Original NDVI Time Series', - interpolateNulls: false, - vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}}, - hAxis: {title: '', format: 'YYYY-MM'}, - lineWidth: 1, - pointSize: 4, - series: { - 0: {color: '#238b45'}, - }, - - }) -print(chart); - -// Gap-filling - -// Add a band containing timestamp to each image -// This will be used to do pixel-wise interpolation later -var originalCollection = originalCollection.map(function(image) { - var timeImage = image.metadata('system:time_start').rename('timestamp') - // The time image doesn't have a mask. - // We set the mask of the time band to be the same as the first band of the image - var timeImageMasked = timeImage.updateMask(image.mask().select(0)) - return image.addBands(timeImageMasked).toFloat(); -}) - -// For each image in the collection, we need to find all images -// before and after the specified time-window - -// This is accomplished using Joins -// We need to do 2 joins -// Join 1: Join the collection with itself to find all images before each image -// Join 2: Join the collection with itself to find all images after each image - -// We first define the filters needed for the join - -// Define a maxDifference filter to find all images within the specified days -// The filter needs the time difference in milliseconds -// Convert days to milliseconds - -// Specify the time-window to look for unmasked pixel -var days = 45; -var millis = ee.Number(days).multiply(1000*60*60*24) - -var maxDiffFilter = ee.Filter.maxDifference({ - difference: millis, - leftField: 'system:time_start', - rightField: 'system:time_start' -}) - -// We need a lessThanOrEquals filter to find all images after a given image -// This will compare the given image's timestamp against other images' timestamps -var lessEqFilter = ee.Filter.lessThanOrEquals({ - leftField: 'system:time_start', - rightField: 'system:time_start' -}) +// Display a time-series chart +var chart = ui.Chart.image.series({ + imageCollection: originalCollection.select('ndvi'), + region: geometry, + reducer: ee.Reducer.mean(), + scale: 20 +}).setOptions({ + title: 'Original NDVI Time Series', + interpolateNulls: false, + vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}}, + hAxis: {title: '', format: 'YYYY-MM'}, + lineWidth: 1, + pointSize: 4, + series: { + 0: {color: '#238b45'}, + }, + + }) +print(chart); + +// Moving-Window Smoothing + +// Specify the time-window +var days = 15; + +// Convert to milliseconds +var millis = ee.Number(days).multiply(1000*60*60*24); + +// We use a 'save-all join' to find all images +// that are within the time-window + +// The join will add all matching images into a +// new property called 'images' +var join = ee.Join.saveAll({ + matchesKey: 'images' +}); + +// This filter will match all images that are captured +// within the specified day of the source image +var diffFilter = ee.Filter.maxDifference({ + difference: millis, + leftField: 'system:time_start', + rightField: 'system:time_start' +}); + + +var joinedCollection = join.apply({ + primary: originalCollection, + secondary: originalCollection, + condition: diffFilter +}); + +print('Joined Collection', joinedCollection); + +// Each image in the joined collection will contain +// matching images in the 'images' property +// Extract and return the mean of matched images +var extractAndComputeMean = function(image) { + var matchingImages = ee.ImageCollection.fromImages(image.get('images')); + var meanImage = matchingImages.reduce( + ee.Reducer.mean().setOutputs(['moving_average'])) + return ee.Image(image).addBands(meanImage) +} -// We need a greaterThanOrEquals filter to find all images before a given image -// This will compare the given image's timestamp against other images' timestamps -var greaterEqFilter = ee.Filter.greaterThanOrEquals({ - leftField: 'system:time_start', - rightField: 'system:time_start' -}) - - -// Apply the joins - -// For the first join, we need to match all images that are after the given image. -// To do this we need to match 2 conditions -// 1. The resulting images must be within the specified time-window of target image -// 2. The target image's timestamp must be lesser than the timestamp of resulting images -// Combine two filters to match both these conditions -var filter1 = ee.Filter.and(maxDiffFilter, lessEqFilter) -// This join will find all images after, sorted in descending order -// This will gives us images so that closest is last -var join1 = ee.Join.saveAll({ - matchesKey: 'after', - ordering: 'system:time_start', - ascending: false}) - -var join1Result = join1.apply({ - primary: originalCollection, - secondary: originalCollection, - condition: filter1 -}) -// Each image now as a property called 'after' containing -// all images that come after it within the time-window -print(join1Result.first()) - -// Do the second join now to match all images within the time-window -// that come before each image -var filter2 = ee.Filter.and(maxDiffFilter, greaterEqFilter) -// This join will find all images before, sorted in ascending order -// This will gives us images so that closest is last -var join2 = ee.Join.saveAll({ - matchesKey: 'before', - ordering: 'system:time_start', - ascending: true}) - -var join2Result = join2.apply({ - primary: join1Result, - secondary: join1Result, - condition: filter2 -}) - -var joinedCol = join2Result; - -// Each image now as a property called 'before' containing -// all images that come after it within the time-window -print(joinedCol.first()) -// Do the gap-filling - -// We now write a function that will be used to interpolate all images -// This function takes an image and replaces the masked pixels -// with the interpolated value from before and after images. - -var interpolateImages = function(image) { - var image = ee.Image(image); - // We get the list of before and after images from the image property - // Mosaic the images so we a before and after image with the closest unmasked pixel - var beforeImages = ee.List(image.get('before')) - var beforeMosaic = ee.ImageCollection.fromImages(beforeImages).mosaic() - var afterImages = ee.List(image.get('after')) - var afterMosaic = ee.ImageCollection.fromImages(afterImages).mosaic() - - // Interpolation formula - // y = y1 + (y2-y1)*((t – t1) / (t2 – t1)) - // y = interpolated image - // y1 = before image - // y2 = after image - // t = interpolation timestamp - // t1 = before image timestamp - // t2 = after image timestamp - - // We first compute the ratio (t – t1) / (t2 – t1) - - // Get image with before and after times - var t1 = beforeMosaic.select('timestamp').rename('t1') - var t2 = afterMosaic.select('timestamp').rename('t2') - - var t = image.metadata('system:time_start').rename('t') - - var timeImage = ee.Image.cat([t1, t2, t]) - - var timeRatio = timeImage.expression('(t - t1) / (t2 - t1)', { - 't': timeImage.select('t'), - 't1': timeImage.select('t1'), - 't2': timeImage.select('t2'), - }) - // You can replace timeRatio with a constant value 0.5 - // if you wanted a simple average - - // Compute an image with the interpolated image y - var interpolated = beforeMosaic - .add((afterMosaic.subtract(beforeMosaic).multiply(timeRatio))) - // Replace the masked pixels in the current image with the average value - var result = image.unmask(interpolated) - return result.copyProperties(image, ['system:time_start']) -} - -// map() the function to gap-fill all images in the collection -var gapFilledCol = ee.ImageCollection(joinedCol.map(interpolateImages)) - -// Display a time-series chart -var chart = ui.Chart.image.series({ - imageCollection: gapFilledCol.select('ndvi'), - region: geometry, - reducer: ee.Reducer.mean(), - scale: 20 -}).setOptions({ - title: 'Gap-Filled NDVI Time Series', - interpolateNulls: false, - vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}}, - hAxis: {title: '', format: 'YYYY-MM'}, - lineWidth: 1, - pointSize: 4, - series: { - 0: {color: '#238b45'}, - }, - }) -print(chart); - -// Let's visualize the NDVI time-series -Map.centerObject(geometry, 16); -var bbox = Map.getBounds({asGeoJSON: true}); - -var palette = ['#d73027','#f46d43','#fdae61','#fee08b','#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850']; -var ndviVis = {min:-0.2, max: 0.8, palette: palette} - -var visualizeImage = function(image) { - return image.visualize(ndviVis).clip(bbox).selfMask() -} - -var visCollectionOriginal = originalCollection.select('ndvi') - .map(visualizeImage) - -var visualizeIGapFilled = gapFilledCol.select('ndvi') - .map(visualizeImage) - - -Export.video.toDrive({ - collection: visCollectionOriginal, - description: 'Original_Time_Series', - folder: 'earthengine', - fileNamePrefix: 'original', - framesPerSecond: 2, - dimensions: 800, - region: bbox}) - -Export.video.toDrive({ - collection: visualizeIGapFilled, - description: 'Gap_Filled_Time_Series', - folder: 'earthengine', - fileNamePrefix: 'gap_filled', - framesPerSecond: 2, - dimensions: 800, - region: bbox})
    +var smoothedCollection = ee.ImageCollection( + joinedCollection.map(extractAndComputeMean)); + +print('Smoothed Collection', smoothedCollection) + +// Display a time-series chart +var chart = ui.Chart.image.series({ + imageCollection: smoothedCollection.select(['ndvi', 'ndvi_moving_average']), + region: geometry, + reducer: ee.Reducer.mean(), + scale: 20 +}).setOptions({ + title: 'NDVI Time Series', + interpolateNulls: false, + vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}}, + hAxis: {title: '', format: 'YYYY-MM'}, + lineWidth: 1, + pointSize: 4, + series: { + 0: {color: '#66c2a4', lineDashStyle: [1, 1], pointSize: 2}, // Original NDVI + 1: {color: '#238b45', lineWidth: 2 }, // Smoothed NDVI + }, + + }) +print(chart); + +// Let's export the NDVI time-series as a video +var palette = ['#d73027','#f46d43','#fdae61','#fee08b', + '#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850']; +var ndviVis = {min:-0.2, max: 0.8, palette: palette} + +Map.centerObject(geometry, 16); +var bbox = Map.getBounds({asGeoJSON: true}); + +var visualizeImage = function(image) { + return image.visualize(ndviVis).clip(bbox).selfMask() +} + +var visCollectionOriginal = originalCollection.select('ndvi') + .map(visualizeImage) + + +var visCollectionSmoothed = smoothedCollection.select('ndvi_moving_average') + .map(visualizeImage) + + +Export.video.toDrive({ + collection: visCollectionOriginal, + description: 'Original_Time_Series', + folder: 'earthengine', + fileNamePrefix: 'original', + framesPerSecond: 2, + dimensions: 800, + region: bbox}) + +Export.video.toDrive({ + collection: visCollectionSmoothed, + description: 'Smoothed_Time_Series', + folder: 'earthengine', + fileNamePrefix: 'smoothed', + framesPerSecond: 2, + dimensions: 800, + region: bbox})
    + +
    +

    Temporal Interpolation

    +

    The code below shows how to do temporal gap-filling of time-series +data. A detailed explanation of the code and other examples are +described in our blog post Temporal Gap-Filling with Linear Interpolation in +GEE.

    +

    Open in Code Editor ↗

    +
    // Temporal Interpolation (Gap-Filling Masked Pixels)
    +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
    +var geometry = ee.Geometry.Point([74.80368345518073, 30.391793042969]);
    +
    +var startDate = ee.Date.fromYMD(2019, 1, 1);
    +var endDate = ee.Date.fromYMD(2021, 1, 1);
    +
    +// Function to add a NDVI band to an image
    +function addNDVI(image) {
    +  var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
    +  return image.addBands(ndvi);
    +} 
    +
    +// Function to mask clouds
    +function maskS2clouds(image) {
    +  var qa = image.select('QA60')
    +  var cloudBitMask = 1 << 10;
    +  var cirrusBitMask = 1 << 11;
    +  var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(
    +             qa.bitwiseAnd(cirrusBitMask).eq(0))
    +  return image.updateMask(mask).divide(10000)
    +      .select("B.*")
    +      .copyProperties(image, ["system:time_start"])
    +}
    +
    +var originalCollection = s2
    +  .filter(ee.Filter.date(startDate, endDate))
    +  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    +  .filter(ee.Filter.bounds(geometry))
    +  .map(maskS2clouds)
    +  .map(addNDVI);
    +
    +
    +// Display a time-series chart
    +var chart = ui.Chart.image.series({
    +  imageCollection: originalCollection.select('ndvi'),
    +  region: geometry,
    +  reducer: ee.Reducer.mean(),
    +  scale: 20
    +}).setOptions({
    +      title: 'Original NDVI Time Series',
    +      interpolateNulls: false,
    +      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    +      hAxis: {title: '', format: 'YYYY-MM'},
    +      lineWidth: 1,
    +      pointSize: 4,
    +      series: {
    +        0: {color: '#238b45'},
    +      },
    +
    +    })
    +print(chart);
    +
    +// Gap-filling
    +
    +// Add a band containing timestamp to each image
    +// This will be used to do pixel-wise interpolation later
    +var originalCollection = originalCollection.map(function(image) {
    +  var timeImage = image.metadata('system:time_start').rename('timestamp')
    +  // The time image doesn't have a mask. 
    +  // We set the mask of the time band to be the same as the first band of the image
    +  var timeImageMasked = timeImage.updateMask(image.mask().select(0))
    +  return image.addBands(timeImageMasked).toFloat();
    +})
    +
    +// For each image in the collection, we need to find all images
    +// before and after the specified time-window
    +
    +// This is accomplished using Joins
    +// We need to do 2 joins
    +// Join 1: Join the collection with itself to find all images before each image
    +// Join 2: Join the collection with itself to find all images after each image
    +
    +// We first define the filters needed for the join
    +
    +// Define a maxDifference filter to find all images within the specified days
    +// The filter needs the time difference in milliseconds
    +// Convert days to milliseconds
    +
    +// Specify the time-window to look for unmasked pixel
    +var days = 45;
    +var millis = ee.Number(days).multiply(1000*60*60*24)
    +
    +var maxDiffFilter = ee.Filter.maxDifference({
    +  difference: millis,
    +  leftField: 'system:time_start',
    +  rightField: 'system:time_start'
    +})
    +
    +// We need a lessThanOrEquals filter to find all images after a given image
    +// This will compare the given image's timestamp against other images' timestamps
    +var lessEqFilter = ee.Filter.lessThanOrEquals({
    +  leftField: 'system:time_start',
    +  rightField: 'system:time_start'
    +})
    +
    +// We need a greaterThanOrEquals filter to find all images before a given image
    +// This will compare the given image's timestamp against other images' timestamps
    +var greaterEqFilter = ee.Filter.greaterThanOrEquals({
    +  leftField: 'system:time_start',
    +  rightField: 'system:time_start'
    +})
    +
    +
    +// Apply the joins
    +
    +// For the first join, we need to match all images that are after the given image.
    +// To do this we need to match 2 conditions
    +// 1. The resulting images must be within the specified time-window of target image
    +// 2. The target image's timestamp must be lesser than the timestamp of resulting images
    +// Combine two filters to match both these conditions
    +var filter1 = ee.Filter.and(maxDiffFilter, lessEqFilter)
    +// This join will find all images after, sorted in descending order
    +// This will gives us images so that closest is last
    +var join1 = ee.Join.saveAll({
    +  matchesKey: 'after',
    +  ordering: 'system:time_start',
    +  ascending: false})
    +  
    +var join1Result = join1.apply({
    +  primary: originalCollection,
    +  secondary: originalCollection,
    +  condition: filter1
    +})
    +// Each image now as a property called 'after' containing
    +// all images that come after it within the time-window
    +print(join1Result.first())
    +
    +// Do the second join now to match all images within the time-window
    +// that come before each image
    +var filter2 = ee.Filter.and(maxDiffFilter, greaterEqFilter)
    +// This join will find all images before, sorted in ascending order
    +// This will gives us images so that closest is last
    +var join2 = ee.Join.saveAll({
    +  matchesKey: 'before',
    +  ordering: 'system:time_start',
    +  ascending: true})
    +  
    +var join2Result = join2.apply({
    +  primary: join1Result,
    +  secondary: join1Result,
    +  condition: filter2
    +})
    +
    +var joinedCol = join2Result;
    +
    +// Each image now as a property called 'before' containing
    +// all images that come after it within the time-window
    +print(joinedCol.first())
    +// Do the gap-filling
    +
    +// We now write a function that will be used to interpolate all images
    +// This function takes an image and replaces the masked pixels
    +// with the interpolated value from before and after images.
    +
    +var interpolateImages = function(image) {
    +  var image = ee.Image(image);
    +  // We get the list of before and after images from the image property
    +  // Mosaic the images so we a before and after image with the closest unmasked pixel
    +  var beforeImages = ee.List(image.get('before'))
    +  var beforeMosaic = ee.ImageCollection.fromImages(beforeImages).mosaic()
    +  var afterImages = ee.List(image.get('after'))
    +  var afterMosaic = ee.ImageCollection.fromImages(afterImages).mosaic()
    +
    +  // Interpolation formula
    +  // y = y1 + (y2-y1)*((t – t1) / (t2 – t1))
    +  // y = interpolated image
    +  // y1 = before image
    +  // y2 = after image
    +  // t = interpolation timestamp
    +  // t1 = before image timestamp
    +  // t2 = after image timestamp
    +  
    +  // We first compute the ratio (t – t1) / (t2 – t1)
    +
    +  // Get image with before and after times
    +  var t1 = beforeMosaic.select('timestamp').rename('t1')
    +  var t2 = afterMosaic.select('timestamp').rename('t2')
    +
    +  var t = image.metadata('system:time_start').rename('t')
    +
    +  var timeImage = ee.Image.cat([t1, t2, t])
    +
    +  var timeRatio = timeImage.expression('(t - t1) / (t2 - t1)', {
    +    't': timeImage.select('t'),
    +    't1': timeImage.select('t1'),
    +    't2': timeImage.select('t2'),
    +  })
    +  // You can replace timeRatio with a constant value 0.5
    +  // if you wanted a simple average
    +  
    +  // Compute an image with the interpolated image y
    +  var interpolated = beforeMosaic
    +    .add((afterMosaic.subtract(beforeMosaic).multiply(timeRatio)))
    +  // Replace the masked pixels in the current image with the average value
    +  var result = image.unmask(interpolated)
    +  return result.copyProperties(image, ['system:time_start'])
    +}
    +
    +// map() the function to gap-fill all images in the collection
    +var gapFilledCol = ee.ImageCollection(joinedCol.map(interpolateImages))
    +  
    +// Display a time-series chart
    +var chart = ui.Chart.image.series({
    +  imageCollection: gapFilledCol.select('ndvi'),
    +  region: geometry,
    +  reducer: ee.Reducer.mean(),
    +  scale: 20
    +}).setOptions({
    +      title: 'Gap-Filled NDVI Time Series',
    +      interpolateNulls: false,
    +      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    +      hAxis: {title: '', format: 'YYYY-MM'},
    +      lineWidth: 1,
    +      pointSize: 4,
    +      series: {
    +        0: {color: '#238b45'},
    +      },
    +    })
    +print(chart);
    +
    +// Let's visualize the NDVI time-series
    +Map.centerObject(geometry, 16);
    +var bbox = Map.getBounds({asGeoJSON: true});
    +
    +var palette = ['#d73027','#f46d43','#fdae61','#fee08b','#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850'];
    +var ndviVis = {min:-0.2, max: 0.8,  palette: palette}
    +
    +var visualizeImage = function(image) {
    +  return image.visualize(ndviVis).clip(bbox).selfMask()
    +}
    +
    +var visCollectionOriginal = originalCollection.select('ndvi')
    +  .map(visualizeImage)
    +
    +var visualizeIGapFilled = gapFilledCol.select('ndvi')
    +  .map(visualizeImage)
    +
    +
    +Export.video.toDrive({
    +  collection: visCollectionOriginal,
    +  description: 'Original_Time_Series',
    +  folder: 'earthengine',
    +  fileNamePrefix: 'original',
    +  framesPerSecond: 2,
    +  dimensions: 800,
    +  region: bbox})
    +
    +Export.video.toDrive({
    +  collection: visualizeIGapFilled,
    +  description: 'Gap_Filled_Time_Series',
    +  folder: 'earthengine',
    +  fileNamePrefix: 'gap_filled',
    +  framesPerSecond: 2,
    +  dimensions: 800,
    +  region: bbox}) 

    Savitzky-Golay Smoothing

    @@ -4037,368 +4129,368 @@

    Savitzky-Golay Smoothing

    Open in Code Editor ↗

    -
    // Aplying Savitzky-Golay Filter on a NDVI Time-Series
    -// This script uses the OEEL library to apply a 
    -// Savitzky-Golay filter on a imagecollection
    -
    -// We require a regularly-spaced time-series without
    -// any masked pixels. So this script applies
    -// linear interpolation to created regularly spaced images
    -// from the original time-series
    -
    -// Step-1: Prepare a NDVI Time-Series
    -// Step-2: Create an empty Time-Series with images at n days
    -// Step-3: Use Joins to find before/after images
    -// Step-4: Apply linear interpolation to fill each image
    -// Step-5: Apply Savitzky-Golay filter
    -// Step-6: Visualize the results
    -
    -var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
    -var geometry = ee.Geometry.Point([74.80368345518073, 30.391793042969]);
    -
    -var startDate = ee.Date.fromYMD(2019, 1, 1);
    -var endDate = ee.Date.fromYMD(2021, 1, 1);
    -
    -// Function to add a NDVI band to an image
    -function addNDVI(image) {
    -  var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
    -  return image.addBands(ndvi);
    -} 
    -
    -// Function to mask clouds
    -function maskS2clouds(image) {
    -  var qa = image.select('QA60')
    -  var cloudBitMask = 1 << 10;
    -  var cirrusBitMask = 1 << 11;
    -  var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(
    -             qa.bitwiseAnd(cirrusBitMask).eq(0))
    -  return image.updateMask(mask).divide(10000)
    -      .select("B.*")
    -      .copyProperties(image, ["system:time_start"])
    -}
    -
    -var originalCollection = s2
    -  .filter(ee.Filter.date(startDate, endDate))
    -  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    -  .filter(ee.Filter.bounds(geometry))
    -  .map(maskS2clouds)
    -  .map(addNDVI);
    -
    -
    -// Display a time-series chart
    -var chart = ui.Chart.image.series({
    -  imageCollection: originalCollection.select('ndvi'),
    -  region: geometry,
    -  reducer: ee.Reducer.mean(),
    -  scale: 20
    -}).setOptions({
    -      title: 'Original NDVI Time Series',
    -      interpolateNulls: false,
    -      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    -      hAxis: {title: '', format: 'YYYY-MM'},
    -      lineWidth: 1,
    -      pointSize: 4,
    -      series: {
    -        0: {color: '#238b45'},
    -      },
    -
    -    })
    -print(chart);
    -
    -// Prepare a regularly-spaced Time-Series
    -
    -// Generate an empty multi-band image matching the bands
    -// in the original collection
    -var bandNames = ee.Image(originalCollection.first()).bandNames();
    -var numBands = bandNames.size();
    -var initBands = ee.List.repeat(ee.Image(), numBands);
    -var initImage = ee.ImageCollection(initBands).toBands().rename(bandNames)
    -
    -// Select the interval. We will have 1 image every n days
    -var n = 5;
    -var firstImage = ee.Image(originalCollection.sort('system:time_start').first())
    -var lastImage = ee.Image(originalCollection.sort('system:time_start', false).first())
    -var timeStart = ee.Date(firstImage.get('system:time_start'))
    -var timeEnd = ee.Date(lastImage.get('system:time_start'))
    -
    -var totalDays = timeEnd.difference(timeStart, 'day');
    -var daysToInterpolate = ee.List.sequence(0, totalDays, n)
    -
    -var initImages = daysToInterpolate.map(function(day) {
    -  var image = initImage.set({
    -    'system:index': ee.Number(day).format('%d'),
    -    'system:time_start': timeStart.advance(day, 'day').millis(),
    -    // Set a property so we can identify interpolated images
    -    'type': 'interpolated'
    -  })
    -  return image
    -})
    -
    -var initCol = ee.ImageCollection.fromImages(initImages)
    -print('Empty Collection', initCol)
    -
    -// Merge original and empty collections
    -var originalCollection = originalCollection.merge(initCol)
    -
    -// Interpolation
    -
    -// Add a band containing timestamp to each image
    -// This will be used to do pixel-wise interpolation later
    -var originalCollection = originalCollection.map(function(image) {
    -  var timeImage = image.metadata('system:time_start').rename('timestamp')
    -  // The time image doesn't have a mask. 
    -  // We set the mask of the time band to be the same as the first band of the image
    -  var timeImageMasked = timeImage.updateMask(image.mask().select(0))
    -  return image.addBands(timeImageMasked).toFloat();
    -})
    -
    -// For each image in the collection, we need to find all images
    -// before and after the specified time-window
    -
    -// This is accomplished using Joins
    -// We need to do 2 joins
    -// Join 1: Join the collection with itself to find all images before each image
    -// Join 2: Join the collection with itself to find all images after each image
    -
    -// We first define the filters needed for the join
    -
    -// Define a maxDifference filter to find all images within the specified days
    -// The filter needs the time difference in milliseconds
    -// Convert days to milliseconds
    -
    -// Specify the time-window to look for unmasked pixel
    -var days = 45;
    -var millis = ee.Number(days).multiply(1000*60*60*24)
    -
    -var maxDiffFilter = ee.Filter.maxDifference({
    -  difference: millis,
    -  leftField: 'system:time_start',
    -  rightField: 'system:time_start'
    -})
    -
    -// We need a lessThanOrEquals filter to find all images after a given image
    -// This will compare the given image's timestamp against other images' timestamps
    -var lessEqFilter = ee.Filter.lessThanOrEquals({
    -  leftField: 'system:time_start',
    -  rightField: 'system:time_start'
    -})
    -
    -// We need a greaterThanOrEquals filter to find all images before a given image
    -// This will compare the given image's timestamp against other images' timestamps
    -var greaterEqFilter = ee.Filter.greaterThanOrEquals({
    -  leftField: 'system:time_start',
    -  rightField: 'system:time_start'
    -})
    -
    -
    -// Apply the joins
    -
    -// For the first join, we need to match all images that are after the given image.
    -// To do this we need to match 2 conditions
    -// 1. The resulting images must be within the specified time-window of target image
    -// 2. The target image's timestamp must be lesser than the timestamp of resulting images
    -// Combine two filters to match both these conditions
    -var filter1 = ee.Filter.and(maxDiffFilter, lessEqFilter)
    -// This join will find all images after, sorted in descending order
    -// This will gives us images so that closest is last
    -var join1 = ee.Join.saveAll({
    -  matchesKey: 'after',
    -  ordering: 'system:time_start',
    -  ascending: false})
    -  
    -var join1Result = join1.apply({
    -  primary: originalCollection,
    -  secondary: originalCollection,
    -  condition: filter1
    -})
    -// Each image now as a property called 'after' containing
    -// all images that come after it within the time-window
    -print(join1Result.first())
    -
    -// Do the second join now to match all images within the time-window
    -// that come before each image
    -var filter2 = ee.Filter.and(maxDiffFilter, greaterEqFilter)
    -// This join will find all images before, sorted in ascending order
    -// This will gives us images so that closest is last
    -var join2 = ee.Join.saveAll({
    -  matchesKey: 'before',
    -  ordering: 'system:time_start',
    -  ascending: true})
    -  
    -var join2Result = join2.apply({
    -  primary: join1Result,
    -  secondary: join1Result,
    -  condition: filter2
    -})
    -
    -// Each image now as a property called 'before' containing
    -// all images that come after it within the time-window
    -print(join2Result.first())
    -
    -var joinedCol = join2Result;
    -
    -// Do the interpolation
    -
    -// We now write a function that will be used to interpolate all images
    -// This function takes an image and replaces the masked pixels
    -// with the interpolated value from before and after images.
    -
    -var interpolateImages = function(image) {
    -  var image = ee.Image(image);
    -  // We get the list of before and after images from the image property
    -  // Mosaic the images so we a before and after image with the closest unmasked pixel
    -  var beforeImages = ee.List(image.get('before'))
    -  var beforeMosaic = ee.ImageCollection.fromImages(beforeImages).mosaic()
    -  var afterImages = ee.List(image.get('after'))
    -  var afterMosaic = ee.ImageCollection.fromImages(afterImages).mosaic()
    -
    -  // Interpolation formula
    -  // y = y1 + (y2-y1)*((t – t1) / (t2 – t1))
    -  // y = interpolated image
    -  // y1 = before image
    -  // y2 = after image
    -  // t = interpolation timestamp
    -  // t1 = before image timestamp
    -  // t2 = after image timestamp
    -  
    -  // We first compute the ratio (t – t1) / (t2 – t1)
    -
    -  // Get image with before and after times
    -  var t1 = beforeMosaic.select('timestamp').rename('t1')
    -  var t2 = afterMosaic.select('timestamp').rename('t2')
    -
    -  var t = image.metadata('system:time_start').rename('t')
    -
    -  var timeImage = ee.Image.cat([t1, t2, t])
    -
    -  var timeRatio = timeImage.expression('(t - t1) / (t2 - t1)', {
    -    't': timeImage.select('t'),
    -    't1': timeImage.select('t1'),
    -    't2': timeImage.select('t2'),
    -  })
    -  // You can replace timeRatio with a constant value 0.5
    -  // if you wanted a simple average
    -  
    -  // Compute an image with the interpolated image y
    -  var interpolated = beforeMosaic
    -    .add((afterMosaic.subtract(beforeMosaic).multiply(timeRatio)))
    -  // Replace the masked pixels in the current image with the average value
    -  var result = image.unmask(interpolated)
    -  return result.copyProperties(image, ['system:time_start'])
    -}
    -
    -// map() the function to interpolate all images in the collection
    -var interpolatedCol = ee.ImageCollection(joinedCol.map(interpolateImages))
    -
    -// Once the interpolation are done, remove original images
    -// We keep only the generated interpolated images
    -var regularCol = interpolatedCol.filter(ee.Filter.eq('type', 'interpolated'))
    -
    -
    -// Display a time-series chart
    -var chart = ui.Chart.image.series({
    -  imageCollection: regularCol.select('ndvi'),
    -  region: geometry,
    -  reducer: ee.Reducer.mean(),
    -  scale: 20
    -}).setOptions({
    -      title: 'Regular NDVI Time Series',
    -      interpolateNulls: false,
    -      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    -      hAxis: {title: '', format: 'YYYY-MM'},
    -      lineWidth: 1,
    -      pointSize: 4,
    -      series: {
    -        0: {color: '#238b45'},
    -      },
    -    })
    -print(chart);
    -
    -
    -// SavatskyGolayFilter
    -// https://www.open-geocomputing.org/OpenEarthEngineLibrary/#.ImageCollection.SavatskyGolayFilter
    -
    -// Use the default distanceFunction
    -var distanceFunction = function(infromedImage, estimationImage) {
    -  return ee.Image.constant(
    -      ee.Number(infromedImage.get('system:time_start'))
    -      .subtract(
    -        ee.Number(estimationImage.get('system:time_start')))
    -        );
    -  }
    -
    -// Apply smoothing
    -
    -var oeel=require('users/OEEL/lib:loadAll');
    -
    -var order = 3;
    -
    -var sgFilteredCol = oeel.ImageCollection.SavatskyGolayFilter(
    -  regularCol, 
    -  maxDiffFilter,
    -  distanceFunction,
    -  order)
    -
    -print(sgFilteredCol.first())
    -// Display a time-series chart
    -var chart = ui.Chart.image.series({
    -  imageCollection: sgFilteredCol.select(['ndvi', 'd_0_ndvi'], ['ndvi', 'ndvi_sg']),
    -  region: geometry,
    -  reducer: ee.Reducer.mean(),
    -  scale: 20
    -}).setOptions({
    -      lineWidth: 1,
    -      title: 'NDVI Time Series',
    -      interpolateNulls: false,
    -      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    -      hAxis: {title: '', format: 'YYYY-MM'},
    -      lineWidth: 1,
    -      pointSize: 4,
    -      series: {
    -        0: {color: '#66c2a4', lineDashStyle: [1, 1], pointSize: 2}, // Original NDVI
    -        1: {color: '#238b45', lineWidth: 2 }, // Smoothed NDVI
    -      },
    -
    -    })
    -print(chart);
    -
    -
    -// Let's visualize the NDVI time-series
    -Map.centerObject(geometry, 16);
    -var bbox = Map.getBounds({asGeoJSON: true});
    -
    -var palette = ['#d73027','#f46d43','#fdae61','#fee08b','#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850'];
    -var ndviVis = {min:-0.2, max: 0.8,  palette: palette}
    -
    -var visualizeImage = function(image) {
    -  return image.visualize(ndviVis).clip(bbox).selfMask()
    -}
    -
    -var visCollectionRegular = regularCol.select('ndvi')
    -  .map(visualizeImage)
    -
    -var visualizeSgFiltered = sgFilteredCol.select('d_0_ndvi')
    -  .map(visualizeImage)
    -
    -
    -Export.video.toDrive({
    -  collection: visCollectionRegular,
    -  description: 'Regular_Time_Series',
    -  folder: 'earthengine',
    -  fileNamePrefix: 'regular',
    -  framesPerSecond: 2,
    -  dimensions: 800,
    -  region: bbox})
    -  
    -Export.video.toDrive({
    -  collection: visualizeSgFiltered,
    -  description: 'Filtered_Time_Series',
    -  folder: 'earthengine',
    -  fileNamePrefix: 'sg_filtered',
    -  framesPerSecond: 2,
    -  dimensions: 800,
    -  region: bbox})
    +
    // Aplying Savitzky-Golay Filter on a NDVI Time-Series
    +// This script uses the OEEL library to apply a 
    +// Savitzky-Golay filter on a imagecollection
    +
    +// We require a regularly-spaced time-series without
    +// any masked pixels. So this script applies
    +// linear interpolation to created regularly spaced images
    +// from the original time-series
    +
    +// Step-1: Prepare a NDVI Time-Series
    +// Step-2: Create an empty Time-Series with images at n days
    +// Step-3: Use Joins to find before/after images
    +// Step-4: Apply linear interpolation to fill each image
    +// Step-5: Apply Savitzky-Golay filter
    +// Step-6: Visualize the results
    +
    +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
    +var geometry = ee.Geometry.Point([74.80368345518073, 30.391793042969]);
    +
    +var startDate = ee.Date.fromYMD(2019, 1, 1);
    +var endDate = ee.Date.fromYMD(2021, 1, 1);
    +
    +// Function to add a NDVI band to an image
    +function addNDVI(image) {
    +  var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
    +  return image.addBands(ndvi);
    +} 
    +
    +// Function to mask clouds
    +function maskS2clouds(image) {
    +  var qa = image.select('QA60')
    +  var cloudBitMask = 1 << 10;
    +  var cirrusBitMask = 1 << 11;
    +  var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(
    +             qa.bitwiseAnd(cirrusBitMask).eq(0))
    +  return image.updateMask(mask).divide(10000)
    +      .select("B.*")
    +      .copyProperties(image, ["system:time_start"])
    +}
    +
    +var originalCollection = s2
    +  .filter(ee.Filter.date(startDate, endDate))
    +  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    +  .filter(ee.Filter.bounds(geometry))
    +  .map(maskS2clouds)
    +  .map(addNDVI);
    +
    +
    +// Display a time-series chart
    +var chart = ui.Chart.image.series({
    +  imageCollection: originalCollection.select('ndvi'),
    +  region: geometry,
    +  reducer: ee.Reducer.mean(),
    +  scale: 20
    +}).setOptions({
    +      title: 'Original NDVI Time Series',
    +      interpolateNulls: false,
    +      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    +      hAxis: {title: '', format: 'YYYY-MM'},
    +      lineWidth: 1,
    +      pointSize: 4,
    +      series: {
    +        0: {color: '#238b45'},
    +      },
    +
    +    })
    +print(chart);
    +
    +// Prepare a regularly-spaced Time-Series
    +
    +// Generate an empty multi-band image matching the bands
    +// in the original collection
    +var bandNames = ee.Image(originalCollection.first()).bandNames();
    +var numBands = bandNames.size();
    +var initBands = ee.List.repeat(ee.Image(), numBands);
    +var initImage = ee.ImageCollection(initBands).toBands().rename(bandNames)
    +
    +// Select the interval. We will have 1 image every n days
    +var n = 5;
    +var firstImage = ee.Image(originalCollection.sort('system:time_start').first())
    +var lastImage = ee.Image(originalCollection.sort('system:time_start', false).first())
    +var timeStart = ee.Date(firstImage.get('system:time_start'))
    +var timeEnd = ee.Date(lastImage.get('system:time_start'))
    +
    +var totalDays = timeEnd.difference(timeStart, 'day');
    +var daysToInterpolate = ee.List.sequence(0, totalDays, n)
    +
    +var initImages = daysToInterpolate.map(function(day) {
    +  var image = initImage.set({
    +    'system:index': ee.Number(day).format('%d'),
    +    'system:time_start': timeStart.advance(day, 'day').millis(),
    +    // Set a property so we can identify interpolated images
    +    'type': 'interpolated'
    +  })
    +  return image
    +})
    +
    +var initCol = ee.ImageCollection.fromImages(initImages)
    +print('Empty Collection', initCol)
    +
    +// Merge original and empty collections
    +var originalCollection = originalCollection.merge(initCol)
    +
    +// Interpolation
    +
    +// Add a band containing timestamp to each image
    +// This will be used to do pixel-wise interpolation later
    +var originalCollection = originalCollection.map(function(image) {
    +  var timeImage = image.metadata('system:time_start').rename('timestamp')
    +  // The time image doesn't have a mask. 
    +  // We set the mask of the time band to be the same as the first band of the image
    +  var timeImageMasked = timeImage.updateMask(image.mask().select(0))
    +  return image.addBands(timeImageMasked).toFloat();
    +})
    +
    +// For each image in the collection, we need to find all images
    +// before and after the specified time-window
    +
    +// This is accomplished using Joins
    +// We need to do 2 joins
    +// Join 1: Join the collection with itself to find all images before each image
    +// Join 2: Join the collection with itself to find all images after each image
    +
    +// We first define the filters needed for the join
    +
    +// Define a maxDifference filter to find all images within the specified days
    +// The filter needs the time difference in milliseconds
    +// Convert days to milliseconds
    +
    +// Specify the time-window to look for unmasked pixel
    +var days = 45;
    +var millis = ee.Number(days).multiply(1000*60*60*24)
    +
    +var maxDiffFilter = ee.Filter.maxDifference({
    +  difference: millis,
    +  leftField: 'system:time_start',
    +  rightField: 'system:time_start'
    +})
    +
    +// We need a lessThanOrEquals filter to find all images after a given image
    +// This will compare the given image's timestamp against other images' timestamps
    +var lessEqFilter = ee.Filter.lessThanOrEquals({
    +  leftField: 'system:time_start',
    +  rightField: 'system:time_start'
    +})
    +
    +// We need a greaterThanOrEquals filter to find all images before a given image
    +// This will compare the given image's timestamp against other images' timestamps
    +var greaterEqFilter = ee.Filter.greaterThanOrEquals({
    +  leftField: 'system:time_start',
    +  rightField: 'system:time_start'
    +})
    +
    +
    +// Apply the joins
    +
    +// For the first join, we need to match all images that are after the given image.
    +// To do this we need to match 2 conditions
    +// 1. The resulting images must be within the specified time-window of target image
    +// 2. The target image's timestamp must be lesser than the timestamp of resulting images
    +// Combine two filters to match both these conditions
    +var filter1 = ee.Filter.and(maxDiffFilter, lessEqFilter)
    +// This join will find all images after, sorted in descending order
    +// This will gives us images so that closest is last
    +var join1 = ee.Join.saveAll({
    +  matchesKey: 'after',
    +  ordering: 'system:time_start',
    +  ascending: false})
    +  
    +var join1Result = join1.apply({
    +  primary: originalCollection,
    +  secondary: originalCollection,
    +  condition: filter1
    +})
    +// Each image now as a property called 'after' containing
    +// all images that come after it within the time-window
    +print(join1Result.first())
    +
    +// Do the second join now to match all images within the time-window
    +// that come before each image
    +var filter2 = ee.Filter.and(maxDiffFilter, greaterEqFilter)
    +// This join will find all images before, sorted in ascending order
    +// This will gives us images so that closest is last
    +var join2 = ee.Join.saveAll({
    +  matchesKey: 'before',
    +  ordering: 'system:time_start',
    +  ascending: true})
    +  
    +var join2Result = join2.apply({
    +  primary: join1Result,
    +  secondary: join1Result,
    +  condition: filter2
    +})
    +
    +// Each image now as a property called 'before' containing
    +// all images that come after it within the time-window
    +print(join2Result.first())
    +
    +var joinedCol = join2Result;
    +
    +// Do the interpolation
    +
    +// We now write a function that will be used to interpolate all images
    +// This function takes an image and replaces the masked pixels
    +// with the interpolated value from before and after images.
    +
    +var interpolateImages = function(image) {
    +  var image = ee.Image(image);
    +  // We get the list of before and after images from the image property
    +  // Mosaic the images so we a before and after image with the closest unmasked pixel
    +  var beforeImages = ee.List(image.get('before'))
    +  var beforeMosaic = ee.ImageCollection.fromImages(beforeImages).mosaic()
    +  var afterImages = ee.List(image.get('after'))
    +  var afterMosaic = ee.ImageCollection.fromImages(afterImages).mosaic()
    +
    +  // Interpolation formula
    +  // y = y1 + (y2-y1)*((t – t1) / (t2 – t1))
    +  // y = interpolated image
    +  // y1 = before image
    +  // y2 = after image
    +  // t = interpolation timestamp
    +  // t1 = before image timestamp
    +  // t2 = after image timestamp
    +  
    +  // We first compute the ratio (t – t1) / (t2 – t1)
    +
    +  // Get image with before and after times
    +  var t1 = beforeMosaic.select('timestamp').rename('t1')
    +  var t2 = afterMosaic.select('timestamp').rename('t2')
    +
    +  var t = image.metadata('system:time_start').rename('t')
    +
    +  var timeImage = ee.Image.cat([t1, t2, t])
    +
    +  var timeRatio = timeImage.expression('(t - t1) / (t2 - t1)', {
    +    't': timeImage.select('t'),
    +    't1': timeImage.select('t1'),
    +    't2': timeImage.select('t2'),
    +  })
    +  // You can replace timeRatio with a constant value 0.5
    +  // if you wanted a simple average
    +  
    +  // Compute an image with the interpolated image y
    +  var interpolated = beforeMosaic
    +    .add((afterMosaic.subtract(beforeMosaic).multiply(timeRatio)))
    +  // Replace the masked pixels in the current image with the average value
    +  var result = image.unmask(interpolated)
    +  return result.copyProperties(image, ['system:time_start'])
    +}
    +
    +// map() the function to interpolate all images in the collection
    +var interpolatedCol = ee.ImageCollection(joinedCol.map(interpolateImages))
    +
    +// Once the interpolation are done, remove original images
    +// We keep only the generated interpolated images
    +var regularCol = interpolatedCol.filter(ee.Filter.eq('type', 'interpolated'))
    +
    +
    +// Display a time-series chart
    +var chart = ui.Chart.image.series({
    +  imageCollection: regularCol.select('ndvi'),
    +  region: geometry,
    +  reducer: ee.Reducer.mean(),
    +  scale: 20
    +}).setOptions({
    +      title: 'Regular NDVI Time Series',
    +      interpolateNulls: false,
    +      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    +      hAxis: {title: '', format: 'YYYY-MM'},
    +      lineWidth: 1,
    +      pointSize: 4,
    +      series: {
    +        0: {color: '#238b45'},
    +      },
    +    })
    +print(chart);
    +
    +
    +// SavatskyGolayFilter
    +// https://www.open-geocomputing.org/OpenEarthEngineLibrary/#.ImageCollection.SavatskyGolayFilter
    +
    +// Use the default distanceFunction
    +var distanceFunction = function(infromedImage, estimationImage) {
    +  return ee.Image.constant(
    +      ee.Number(infromedImage.get('system:time_start'))
    +      .subtract(
    +        ee.Number(estimationImage.get('system:time_start')))
    +        );
    +  }
    +
    +// Apply smoothing
    +
    +var oeel=require('users/OEEL/lib:loadAll');
    +
    +var order = 3;
    +
    +var sgFilteredCol = oeel.ImageCollection.SavatskyGolayFilter(
    +  regularCol, 
    +  maxDiffFilter,
    +  distanceFunction,
    +  order)
    +
    +print(sgFilteredCol.first())
    +// Display a time-series chart
    +var chart = ui.Chart.image.series({
    +  imageCollection: sgFilteredCol.select(['ndvi', 'd_0_ndvi'], ['ndvi', 'ndvi_sg']),
    +  region: geometry,
    +  reducer: ee.Reducer.mean(),
    +  scale: 20
    +}).setOptions({
    +      lineWidth: 1,
    +      title: 'NDVI Time Series',
    +      interpolateNulls: false,
    +      vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
    +      hAxis: {title: '', format: 'YYYY-MM'},
    +      lineWidth: 1,
    +      pointSize: 4,
    +      series: {
    +        0: {color: '#66c2a4', lineDashStyle: [1, 1], pointSize: 2}, // Original NDVI
    +        1: {color: '#238b45', lineWidth: 2 }, // Smoothed NDVI
    +      },
    +
    +    })
    +print(chart);
    +
    +
    +// Let's visualize the NDVI time-series
    +Map.centerObject(geometry, 16);
    +var bbox = Map.getBounds({asGeoJSON: true});
    +
    +var palette = ['#d73027','#f46d43','#fdae61','#fee08b','#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850'];
    +var ndviVis = {min:-0.2, max: 0.8,  palette: palette}
    +
    +var visualizeImage = function(image) {
    +  return image.visualize(ndviVis).clip(bbox).selfMask()
    +}
    +
    +var visCollectionRegular = regularCol.select('ndvi')
    +  .map(visualizeImage)
    +
    +var visualizeSgFiltered = sgFilteredCol.select('d_0_ndvi')
    +  .map(visualizeImage)
    +
    +
    +Export.video.toDrive({
    +  collection: visCollectionRegular,
    +  description: 'Regular_Time_Series',
    +  folder: 'earthengine',
    +  fileNamePrefix: 'regular',
    +  framesPerSecond: 2,
    +  dimensions: 800,
    +  region: bbox})
    +  
    +Export.video.toDrive({
    +  collection: visualizeSgFiltered,
    +  description: 'Filtered_Time_Series',
    +  folder: 'earthengine',
    +  fileNamePrefix: 'sg_filtered',
    +  framesPerSecond: 2,
    +  dimensions: 800,
    +  region: bbox})
    @@ -4417,46 +4509,46 @@

    Adding a Discrete Legend

    Open in Code Editor ↗

    -
    var classified = ee.Image("users/ujavalgandhi/e2e/bangalore_classified")
    -Map.centerObject(classified)
    -var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40'];
    -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019');
    -
    -var legend = ui.Panel({style: {position: 'middle-right', padding: '8px 15px'}});
    -
    -var makeRow = function(color, name) {
    -  var colorBox = ui.Label({
    -    style: {color: '#ffffff',
    -      backgroundColor: color,
    -      padding: '10px',
    -      margin: '0 0 4px 0',
    -    }
    -  });
    -  var description = ui.Label({
    -    value: name,
    -    style: {
    -      margin: '0px 0 4px 6px',
    -    }
    -  }); 
    -  return ui.Panel({
    -    widgets: [colorBox, description],
    -    layout: ui.Panel.Layout.Flow('horizontal')}
    -)};
    -
    -var title = ui.Label({
    -  value: 'Legend',
    -  style: {fontWeight: 'bold',
    -    fontSize: '16px',
    -    margin: '0px 0 4px 0px'}});
    -
    -legend.add(title);
    -legend.add(makeRow('#cc6d8f','Built-up'))
    -legend.add(makeRow('#ffc107','Bare Earth'))
    -legend.add(makeRow('#1e88e5','Water'))
    -legend.add(makeRow('#004d40','Vegetation'))
    -
    -Map.add(legend);
    +
    var classified = ee.Image("users/ujavalgandhi/e2e/bangalore_classified")
    +Map.centerObject(classified)
    +var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40'];
    +Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019');
    +
    +var legend = ui.Panel({style: {position: 'middle-right', padding: '8px 15px'}});
    +
    +var makeRow = function(color, name) {
    +  var colorBox = ui.Label({
    +    style: {color: '#ffffff',
    +      backgroundColor: color,
    +      padding: '10px',
    +      margin: '0 0 4px 0',
    +    }
    +  });
    +  var description = ui.Label({
    +    value: name,
    +    style: {
    +      margin: '0px 0 4px 6px',
    +    }
    +  }); 
    +  return ui.Panel({
    +    widgets: [colorBox, description],
    +    layout: ui.Panel.Layout.Flow('horizontal')}
    +)};
    +
    +var title = ui.Label({
    +  value: 'Legend',
    +  style: {fontWeight: 'bold',
    +    fontSize: '16px',
    +    margin: '0px 0 4px 0px'}});
    +
    +legend.add(title);
    +legend.add(makeRow('#cc6d8f','Built-up'))
    +legend.add(makeRow('#ffc107','Bare Earth'))
    +legend.add(makeRow('#1e88e5','Water'))
    +legend.add(makeRow('#004d40','Vegetation'))
    +
    +Map.add(legend);

    Adding a Continous Legend

    @@ -4472,66 +4564,66 @@

    Adding a Continous Legend

    Open in Code Editor ↗

    -
    var s2 = ee.ImageCollection("COPERNICUS/S2");
    -var admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2");
    -
    -var bangalore = admin2.filter(ee.Filter.eq('ADM2_NAME', 'Bangalore Urban'))
    -var geometry = bangalore.geometry()
    -
    -var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    -  .filter(ee.Filter.date('2019-01-01', '2020-01-01'))
    -  .filter(ee.Filter.bounds(geometry))
    -
    -var image = filtered.median(); 
    -
    -// Calculate  Normalized Difference Vegetation Index (NDVI)
    -// 'NIR' (B8) and 'RED' (B4)
    -var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']);
    -
    -var palette = ['#d7191c','#fdae61','#ffffbf','#a6d96a','#1a9641']
    -var ndviVis = {min:0, max:0.5, palette: palette}
    -Map.centerObject(geometry, 12)
    -Map.addLayer(ndvi.clip(geometry), ndviVis, 'ndvi')
    -
    -
    -function createColorBar(titleText, palette, min, max) {
    -  // Legend Title
    -  var title = ui.Label({
    -    value: titleText, 
    -    style: {fontWeight: 'bold', textAlign: 'center', stretch: 'horizontal'}});
    -
    -  // Colorbar
    -  var legend = ui.Thumbnail({
    -    image: ee.Image.pixelLonLat().select(0),
    -    params: {
    -      bbox: [0, 0, 1, 0.1],
    -      dimensions: '200x20',
    -      format: 'png', 
    -      min: 0, max: 1,
    -      palette: palette},
    -    style: {stretch: 'horizontal', margin: '8px 8px', maxHeight: '40px'},
    -  });
    -  
    -  // Legend Labels
    -  var labels = ui.Panel({
    -    widgets: [
    -      ui.Label(min, {margin: '4px 10px',textAlign: 'left', stretch: 'horizontal'}),
    -      ui.Label((min+max)/2, {margin: '4px 20px', textAlign: 'center', stretch: 'horizontal'}),
    -      ui.Label(max, {margin: '4px 10px',textAlign: 'right', stretch: 'horizontal'})],
    -    layout: ui.Panel.Layout.flow('horizontal')});
    -  
    -  // Create a panel with all 3 widgets
    -  var legendPanel = ui.Panel({
    -    widgets: [title, legend, labels],
    -    style: {position: 'bottom-center', padding: '8px 15px'}
    -  })
    -  return legendPanel
    -}
    -// Call the function to create a colorbar legend  
    -var colorBar = createColorBar('NDVI Values', palette, 0, 0.5)
    -
    -Map.add(colorBar)
    +
    var s2 = ee.ImageCollection("COPERNICUS/S2");
    +var admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2");
    +
    +var bangalore = admin2.filter(ee.Filter.eq('ADM2_NAME', 'Bangalore Urban'))
    +var geometry = bangalore.geometry()
    +
    +var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    +  .filter(ee.Filter.date('2019-01-01', '2020-01-01'))
    +  .filter(ee.Filter.bounds(geometry))
    +
    +var image = filtered.median(); 
    +
    +// Calculate  Normalized Difference Vegetation Index (NDVI)
    +// 'NIR' (B8) and 'RED' (B4)
    +var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']);
    +
    +var palette = ['#d7191c','#fdae61','#ffffbf','#a6d96a','#1a9641']
    +var ndviVis = {min:0, max:0.5, palette: palette}
    +Map.centerObject(geometry, 12)
    +Map.addLayer(ndvi.clip(geometry), ndviVis, 'ndvi')
    +
    +
    +function createColorBar(titleText, palette, min, max) {
    +  // Legend Title
    +  var title = ui.Label({
    +    value: titleText, 
    +    style: {fontWeight: 'bold', textAlign: 'center', stretch: 'horizontal'}});
    +
    +  // Colorbar
    +  var legend = ui.Thumbnail({
    +    image: ee.Image.pixelLonLat().select(0),
    +    params: {
    +      bbox: [0, 0, 1, 0.1],
    +      dimensions: '200x20',
    +      format: 'png', 
    +      min: 0, max: 1,
    +      palette: palette},
    +    style: {stretch: 'horizontal', margin: '8px 8px', maxHeight: '40px'},
    +  });
    +  
    +  // Legend Labels
    +  var labels = ui.Panel({
    +    widgets: [
    +      ui.Label(min, {margin: '4px 10px',textAlign: 'left', stretch: 'horizontal'}),
    +      ui.Label((min+max)/2, {margin: '4px 20px', textAlign: 'center', stretch: 'horizontal'}),
    +      ui.Label(max, {margin: '4px 10px',textAlign: 'right', stretch: 'horizontal'})],
    +    layout: ui.Panel.Layout.flow('horizontal')});
    +  
    +  // Create a panel with all 3 widgets
    +  var legendPanel = ui.Panel({
    +    widgets: [title, legend, labels],
    +    style: {position: 'bottom-center', padding: '8px 15px'}
    +  })
    +  return legendPanel
    +}
    +// Call the function to create a colorbar legend  
    +var colorBar = createColorBar('NDVI Values', palette, 0, 0.5)
    +
    +Map.add(colorBar)

    Change Visualization UI App

    @@ -4552,49 +4644,49 @@

    Change Visualization UI App

    Open in Code Editor ↗

    -
    // On June 9, 2018 - A massive dust storm hit North India
    -// This example shows before and after imagery from Sentinel-2
    -
    -// Display two visualizations of a map.
    -
    -// Set a center and zoom level.
    -var center = {lon: 77.47, lat: 28.41, zoom: 12};
    -
    -// Create two maps.
    -var leftMap = ui.Map(center);
    -var rightMap = ui.Map(center);
    -
    -// Remove UI controls from both maps, but leave zoom control on the left map.
    -leftMap.setControlVisibility(false);
    -rightMap.setControlVisibility(false);
    -leftMap.setControlVisibility({zoomControl: true});
    -
    -// Link them together.
    -var linker = new ui.Map.Linker([leftMap, rightMap]);
    -
    -// Create a split panel with the two maps.
    -var splitPanel = ui.SplitPanel({
    -  firstPanel: leftMap,
    -  secondPanel: rightMap,
    -  orientation: 'horizontal',
    -  wipe: true
    -});
    -
    -// Remove the default map from the root panel.
    -ui.root.clear();
    -
    -// Add our split panel to the root panel.
    -ui.root.add(splitPanel);
    -
    -var rgb_vis = {min: 0, max: 3200, bands: ['B4', 'B3', 'B2']};
    -
    -var preStorm = ee.Image('COPERNICUS/S2/20180604T052651_20180604T053435_T43RGM')
    -var postStorm = ee.Image('COPERNICUS/S2/20180614T052651_20180614T053611_T43RGM')
    -
    -// Add a RGB Landsat 8 layer to the left map.
    -leftMap.addLayer(preStorm, rgb_vis);
    -rightMap.addLayer(postStorm, rgb_vis);
    +
    // On June 9, 2018 - A massive dust storm hit North India
    +// This example shows before and after imagery from Sentinel-2
    +
    +// Display two visualizations of a map.
    +
    +// Set a center and zoom level.
    +var center = {lon: 77.47, lat: 28.41, zoom: 12};
    +
    +// Create two maps.
    +var leftMap = ui.Map(center);
    +var rightMap = ui.Map(center);
    +
    +// Remove UI controls from both maps, but leave zoom control on the left map.
    +leftMap.setControlVisibility(false);
    +rightMap.setControlVisibility(false);
    +leftMap.setControlVisibility({zoomControl: true});
    +
    +// Link them together.
    +var linker = new ui.Map.Linker([leftMap, rightMap]);
    +
    +// Create a split panel with the two maps.
    +var splitPanel = ui.SplitPanel({
    +  firstPanel: leftMap,
    +  secondPanel: rightMap,
    +  orientation: 'horizontal',
    +  wipe: true
    +});
    +
    +// Remove the default map from the root panel.
    +ui.root.clear();
    +
    +// Add our split panel to the root panel.
    +ui.root.add(splitPanel);
    +
    +var rgb_vis = {min: 0, max: 3200, bands: ['B4', 'B3', 'B2']};
    +
    +var preStorm = ee.Image('COPERNICUS/S2/20180604T052651_20180604T053435_T43RGM')
    +var postStorm = ee.Image('COPERNICUS/S2/20180614T052651_20180614T053611_T43RGM')
    +
    +// Add a RGB Landsat 8 layer to the left map.
    +leftMap.addLayer(preStorm, rgb_vis);
    +rightMap.addLayer(postStorm, rgb_vis);

    NDVI Explorer UI App

    @@ -4611,158 +4703,158 @@

    NDVI Explorer UI App

    Open in Code Editor ↗

    -
    var geometry = ee.Geometry.Point([77.5979, 13.00896]);
    -Map.centerObject(geometry, 10)
    -
    -var s2 = ee.ImageCollection("COPERNICUS/S2")
    -var rgbVis = {
    -  min: 0.0,
    -  max: 0.3,
    -  bands: ['B4', 'B3', 'B2'],
    -};
    -
    -var palette = [
    -  'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718',
    -  '74A901', '66A000', '529400', '3E8601', '207401', '056201',
    -  '004C00', '023B01', '012E01', '011D01', '011301'];
    -
    -var ndviVis = {min:0, max:0.5, palette: palette }
    -
    -
    -// Write a function for Cloud masking
    -function maskS2clouds(image) {
    -  var qa = image.select('QA60')
    -  var cloudBitMask = 1 << 10;
    -  var cirrusBitMask = 1 << 11;
    -  var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(
    -             qa.bitwiseAnd(cirrusBitMask).eq(0))
    -  return image.updateMask(mask).divide(10000)
    -      .select("B.*")
    -      .copyProperties(image, ["system:time_start"])
    -}
    -
    -
    -// Write a function that computes NDVI for an image and adds it as a band
    -function addNDVI(image) {
    -  var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
    -  return image.addBands(ndvi);
    -}
    -
    -function getComposite(geometry) {
    -  var filtered = s2
    -  .filter(ee.Filter.date('2019-01-01', '2019-12-31'))
    -  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    -  .filter(ee.Filter.bounds(geometry))
    -  .map(maskS2clouds)
    -  // Map the function over the collection
    -  var withNdvi = filtered.map(addNDVI);
    -
    -  var composite = withNdvi.median()
    -  return composite
    -}
    -
    -
    -// Create UI Elements
    -var title = ui.Label('Global NDVI Explorer');
    -title.style().set({
    -  'position':  'top-center',
    -  'fontSize': '24px'
    -  });
    -var resultsPanel = ui.Panel();
    -var chartPanel = ui.Panel();
    -var selectionPanel = ui.Panel({
    -  layout: ui.Panel.Layout.flow('horizontal'),
    -});
    -
    -resultsPanel.style().set({
    -  width: '400px',
    -  position: 'bottom-right'
    -});
    -
    -var resetPanel = ui.Panel();
    -
    -
    -resultsPanel.add(selectionPanel)
    -resultsPanel.add(chartPanel)
    -resultsPanel.add(resetPanel)
    -
    -// Function to reset the app to initial state
    -var resetEverything = function() {
    -  chartPanel.clear()
    -  selectionPanel.clear()
    -  resetPanel.clear()
    -
    -  Map.clear()
    -
    -  Map.add(title);
    -  Map.add(resultsPanel)
    -  Map.onClick(displayChart)
    -  // Use the current viewport
    -  var bounds = ee.Geometry.Rectangle(Map.getBounds())
    -  var composite = getComposite(bounds)
    -  Map.addLayer(composite, rgbVis, 'Sentinel-2 Composite')
    -  var label = ui.Label('Click anywhere to see the chart')
    -  resetPanel.add(label)
    -
    -}
    -
    -// Function to create and display NDVI time-series chart
    -var displayChart = function(point) {
    -  resetPanel.clear()
    -  var button = ui.Button({
    -    label: 'Reset',
    -    onClick: resetEverything})
    -  resetPanel.add(button)
    -  var geometry = ee.Geometry.Point(point['lon'], point['lat']);
    -  
    -  var filtered = s2
    -    .filter(ee.Filter.date('2019-01-01', '2019-12-31'))
    -    .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 50))
    -    .map(maskS2clouds)
    -    .map(addNDVI)
    -    .filter(ee.Filter.bounds(geometry))
    -  
    -  var chart = ui.Chart.image.series({
    -    imageCollection: filtered.select('ndvi'),
    -    region: geometry,
    -    reducer: ee.Reducer.mean(),
    -    scale: 20}).setOptions({
    -      title: 'NDVI Time Series',
    -      vAxis: {title: 'NDVI'},
    -      hAxis: {title: 'Date', gridlines: {count: 12}}
    -    })
    -      
    -  chartPanel.clear()
    -  selectionPanel.clear()
    -  selectionPanel.add(ui.Label('Choose an image to display:'))
    -  chartPanel.add(chart)
    -  
    -  
    -  var addNdviLayer = function(dateString) {
    -    var date = ee.Date.parse('YYYY-MM-dd', dateString)
    -    var image = ee.Image(filtered.filter(ee.Filter.date(date, date.advance(1, 'day'))).mosaic())
    -    Map.addLayer(image.select('ndvi'), ndviVis, 'NDVI Image -' + dateString)
    -  }
    -
    -
    -  filtered.aggregate_array('system:time_start').evaluate(function(ids) {
    -    var dates = ee.List(ids).distinct().map(function(timestamp) {
    -      return ee.Date(timestamp).format('YYYY-MM-dd')
    -    })
    -    dates.evaluate(function(dateList){
    -      selectionPanel.add(ui.Select({
    -      items: dateList,
    -      onChange: addNdviLayer,
    -      placeholder: 'Select a date'
    -    }))
    -    })
    -
    -});
    -
    -}
    -// Call the function to build the initial UI state.
    -resetEverything();
    +
    var geometry = ee.Geometry.Point([77.5979, 13.00896]);
    +Map.centerObject(geometry, 10)
    +
    +var s2 = ee.ImageCollection("COPERNICUS/S2")
    +var rgbVis = {
    +  min: 0.0,
    +  max: 0.3,
    +  bands: ['B4', 'B3', 'B2'],
    +};
    +
    +var palette = [
    +  'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718',
    +  '74A901', '66A000', '529400', '3E8601', '207401', '056201',
    +  '004C00', '023B01', '012E01', '011D01', '011301'];
    +
    +var ndviVis = {min:0, max:0.5, palette: palette }
    +
    +
    +// Write a function for Cloud masking
    +function maskS2clouds(image) {
    +  var qa = image.select('QA60')
    +  var cloudBitMask = 1 << 10;
    +  var cirrusBitMask = 1 << 11;
    +  var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(
    +             qa.bitwiseAnd(cirrusBitMask).eq(0))
    +  return image.updateMask(mask).divide(10000)
    +      .select("B.*")
    +      .copyProperties(image, ["system:time_start"])
    +}
    +
    +
    +// Write a function that computes NDVI for an image and adds it as a band
    +function addNDVI(image) {
    +  var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
    +  return image.addBands(ndvi);
    +}
    +
    +function getComposite(geometry) {
    +  var filtered = s2
    +  .filter(ee.Filter.date('2019-01-01', '2019-12-31'))
    +  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
    +  .filter(ee.Filter.bounds(geometry))
    +  .map(maskS2clouds)
    +  // Map the function over the collection
    +  var withNdvi = filtered.map(addNDVI);
    +
    +  var composite = withNdvi.median()
    +  return composite
    +}
    +
    +
    +// Create UI Elements
    +var title = ui.Label('Global NDVI Explorer');
    +title.style().set({
    +  'position':  'top-center',
    +  'fontSize': '24px'
    +  });
    +var resultsPanel = ui.Panel();
    +var chartPanel = ui.Panel();
    +var selectionPanel = ui.Panel({
    +  layout: ui.Panel.Layout.flow('horizontal'),
    +});
    +
    +resultsPanel.style().set({
    +  width: '400px',
    +  position: 'bottom-right'
    +});
    +
    +var resetPanel = ui.Panel();
    +
    +
    +resultsPanel.add(selectionPanel)
    +resultsPanel.add(chartPanel)
    +resultsPanel.add(resetPanel)
    +
    +// Function to reset the app to initial state
    +var resetEverything = function() {
    +  chartPanel.clear()
    +  selectionPanel.clear()
    +  resetPanel.clear()
    +
    +  Map.clear()
    +
    +  Map.add(title);
    +  Map.add(resultsPanel)
    +  Map.onClick(displayChart)
    +  // Use the current viewport
    +  var bounds = ee.Geometry.Rectangle(Map.getBounds())
    +  var composite = getComposite(bounds)
    +  Map.addLayer(composite, rgbVis, 'Sentinel-2 Composite')
    +  var label = ui.Label('Click anywhere to see the chart')
    +  resetPanel.add(label)
    +
    +}
    +
    +// Function to create and display NDVI time-series chart
    +var displayChart = function(point) {
    +  resetPanel.clear()
    +  var button = ui.Button({
    +    label: 'Reset',
    +    onClick: resetEverything})
    +  resetPanel.add(button)
    +  var geometry = ee.Geometry.Point(point['lon'], point['lat']);
    +  
    +  var filtered = s2
    +    .filter(ee.Filter.date('2019-01-01', '2019-12-31'))
    +    .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 50))
    +    .map(maskS2clouds)
    +    .map(addNDVI)
    +    .filter(ee.Filter.bounds(geometry))
    +  
    +  var chart = ui.Chart.image.series({
    +    imageCollection: filtered.select('ndvi'),
    +    region: geometry,
    +    reducer: ee.Reducer.mean(),
    +    scale: 20}).setOptions({
    +      title: 'NDVI Time Series',
    +      vAxis: {title: 'NDVI'},
    +      hAxis: {title: 'Date', gridlines: {count: 12}}
    +    })
    +      
    +  chartPanel.clear()
    +  selectionPanel.clear()
    +  selectionPanel.add(ui.Label('Choose an image to display:'))
    +  chartPanel.add(chart)
    +  
    +  
    +  var addNdviLayer = function(dateString) {
    +    var date = ee.Date.parse('YYYY-MM-dd', dateString)
    +    var image = ee.Image(filtered.filter(ee.Filter.date(date, date.advance(1, 'day'))).mosaic())
    +    Map.addLayer(image.select('ndvi'), ndviVis, 'NDVI Image -' + dateString)
    +  }
    +
    +
    +  filtered.aggregate_array('system:time_start').evaluate(function(ids) {
    +    var dates = ee.List(ids).distinct().map(function(timestamp) {
    +      return ee.Date(timestamp).format('YYYY-MM-dd')
    +    })
    +    dates.evaluate(function(dateList){
    +      selectionPanel.add(ui.Select({
    +      items: dateList,
    +      onChange: addNdviLayer,
    +      placeholder: 'Select a date'
    +    }))
    +    })
    +
    +});
    +
    +}
    +// Call the function to build the initial UI state.
    +resetEverything();
    @@ -4863,29 +4955,29 @@

    Sharing Code between Scripts

    Open in Code Editor ↗

    -
    var karnataka = ee.FeatureCollection("users/ujavalgandhi/public/karnataka");
    -Map.addLayer(karnataka, {color: 'gray'}, 'State Boundary')
    -var bounds = karnataka.geometry().bounds()
    -var coords = ee.List(bounds.coordinates().get(0))
    -var xmin = ee.List(coords.get(0)).get(0)
    -var ymin = ee.List(coords.get(0)).get(1)
    -var xmax = ee.List(coords.get(2)).get(0)
    -var ymax = ee.List(coords.get(2)).get(1)
    -// source code for the grid package:
    -// https://code.earthengine.google.com/?accept_repo=users/gena/packages
    -
    -// Import the module to our script using 'require'
    -var gridModule = require('users/gena/packages:grid')
    -
    -// Now we can run any function from the module
    -// We try running the generateGrid function to create regularly spaced vector grid
    -// generateGrid(xmin, ymin, xmax, ymax, dx, dy, marginx, marginy, opt_proj)
    -
    -var spacing = 0.5
    -var gridVector = gridModule.generateGrid(xmin, ymin, xmax, ymax, spacing, spacing, 0, 0)
    -Map.centerObject(gridVector)
    -Map.addLayer(gridVector, {color: 'blue'}, 'Grids')
    +
    var karnataka = ee.FeatureCollection("users/ujavalgandhi/public/karnataka");
    +Map.addLayer(karnataka, {color: 'gray'}, 'State Boundary')
    +var bounds = karnataka.geometry().bounds()
    +var coords = ee.List(bounds.coordinates().get(0))
    +var xmin = ee.List(coords.get(0)).get(0)
    +var ymin = ee.List(coords.get(0)).get(1)
    +var xmax = ee.List(coords.get(2)).get(0)
    +var ymax = ee.List(coords.get(2)).get(1)
    +// source code for the grid package:
    +// https://code.earthengine.google.com/?accept_repo=users/gena/packages
    +
    +// Import the module to our script using 'require'
    +var gridModule = require('users/gena/packages:grid')
    +
    +// Now we can run any function from the module
    +// We try running the generateGrid function to create regularly spaced vector grid
    +// generateGrid(xmin, ymin, xmax, ymax, dx, dy, marginx, marginy, opt_proj)
    +
    +var spacing = 0.5
    +var gridVector = gridModule.generateGrid(xmin, ymin, xmax, ymax, spacing, spacing, 0, 0)
    +Map.centerObject(gridVector)
    +Map.addLayer(gridVector, {color: 'blue'}, 'Grids')
    diff --git a/docs/end-to-end-gee.html b/docs/end-to-end-gee.html index bfa698ee..5d47d0b2 100644 --- a/docs/end-to-end-gee.html +++ b/docs/end-to-end-gee.html @@ -1689,7 +1689,7 @@

    03. Computation on ImageCollections

    var composite = withNdvi.median(); -var ndviComposite = composite.select('ndvi').clip(geometry); +var ndviComposite = composite.select('ndvi'); var palette = [ 'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', @@ -1697,7 +1697,7 @@

    03. Computation on ImageCollections

    '004C00', '023B01', '012E01', '011D01', '011301']; var ndviVis = {min:0, max:0.5, palette: palette }; -Map.addLayer(ndviComposite, ndviVis, 'ndvi'); +Map.addLayer(ndviComposite.clip(geometry), ndviVis, 'ndvi');

    Exercise

    Exercise var withIndices = filtered.map(addIndices); // Composite -var composite = withIndices.median().clip(geometry); +var composite = withIndices.median(); print(composite); -// Extract the 'ndwi' band and display a NDWI map -// use the palette ['white', 'blue'] -// Hint: Use .select() function to select a band

    +// Extract the 'ndwi' band +// Clip and display a NDWI map +// use the palette ['white', 'blue'] +// Hint: Use .select() function to select a band
    @@ -2146,7 +2147,7 @@

    01. Basic Supervised Classification

    .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. var rgbVis = { @@ -2154,7 +2155,7 @@

    01. Basic Supervised Classification

    max: 3000, bands: ['B4', 'B3', 'B2'], }; -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); var gcps = urban.merge(bare).merge(water).merge(vegetation); @@ -2179,7 +2180,7 @@

    01. Basic Supervised Classification

    // Urban, Bare, Water, Vegetation var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); // Display the GCPs // We use the style() function to style the GCPs var palette = ee.List(palette); @@ -2222,12 +2223,12 @@

    Exercise

    .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. var rgbVis = {min: 0.0, max: 3000, bands: ['B4', 'B3', 'B2']}; -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Exercise // Add training points for 4 classes @@ -2266,7 +2267,7 @@

    Exercise

    // // Urban, Bare, Water, Vegetation // var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -// Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019');
    +// Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019');
    @@ -2328,10 +2329,10 @@

    02. Accuracy Assessment

    .filter(ee.Filter.bounds(geometry)) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); // Display the input composite. -Map.addLayer(composite, rgbVis, 'image'); +Map.addLayer(composite.clip(geometry), rgbVis, 'image'); // Add a random column and split the GCPs into training and validation set @@ -2363,7 +2364,7 @@

    02. Accuracy Assessment

    var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment //************************************************************************** @@ -2526,7 +2527,7 @@

    03. Improving the Classification

    .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry); +var composite = filtered.median(); var addIndices = function(image) { @@ -2553,7 +2554,7 @@

    03. Improving the Classification

    var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -2621,7 +2622,7 @@

    03. Improving the Classification

    var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment @@ -2736,7 +2737,7 @@

    04. Exporting Classification Results

    .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); var addIndices = function(image) { var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']); @@ -2762,7 +2763,7 @@

    04. Exporting Classification Results

    var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -2831,7 +2832,7 @@

    04. Exporting Classification Results

    var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); //************************************************************************** // Accuracy Assessment @@ -2921,7 +2922,7 @@

    Exercise

    .map(maskCloudAndShadowsSR) .select('B.*'); -var composite = filtered.median().clip(geometry) ; +var composite = filtered.median(); var addIndices = function(image) { var ndvi = image.normalizedDifference(['B8', 'B4']).rename(['ndvi']); @@ -2947,7 +2948,7 @@

    Exercise

    var composite = composite.addBands(elev).addBands(slope); var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2}; -Map.addLayer(composite, visParams, 'RGB'); +Map.addLayer(composite.clip(geometry), visParams, 'RGB'); // Normalize the image @@ -3016,7 +3017,7 @@

    Exercise

    var classified = composite.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; -Map.addLayer(classified, {min: 0, max: 3, palette: palette}, '2019'); +Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '2019'); // Exercise // Use the Export.image.toAsset() function to export the @@ -3587,18 +3588,18 @@

    03. Direct Classification of Change

    .map(maskS2clouds); -var image2019 = filtered.median().clip(geometry); +var image2019 = filtered.median(); // Display the input composite. -Map.addLayer(image2019, rgbVis, '2019'); +Map.addLayer(image2019.clip(geometry), rgbVis, '2019'); var filtered = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(bangalore)) .map(maskS2clouds); -var image2020 = filtered.median().clip(geometry); +var image2020 = filtered.median(); -Map.addLayer(image2020, rgbVis, '2020'); +Map.addLayer(image2020.clip(geometry), rgbVis, '2020'); var stackedImage = image2019.addBands(image2020); @@ -3618,7 +3619,7 @@

    03. Direct Classification of Change

    // Classify the image. var classified = stackedImage.classify(classifier); -Map.addLayer(classified, {min: 0, max: 1, palette: ['white', 'red']}, 'change');
    +Map.addLayer(classified.clip(geometry), {min: 0, max: 1, palette: ['white', 'red']}, 'change');

    Exercise

    04. Post-classification Comparison var before = filtered.median().clip(geometry); // Display the input composite. -Map.addLayer(before, rgbVis, 'before'); +Map.addLayer(before.clip(geometry), rgbVis, 'before'); var training = urban.merge(bare).merge(water).merge(vegetation); @@ -3695,75 +3696,74 @@

    04. Post-classification Comparison

    var beforeClassified = before.classify(classifier); var palette = ['#cc6d8f', '#ffc107', '#1e88e5', '#004d40' ]; var classifiedVis = {min: 0, max: 3, palette: palette}; -Map.addLayer(beforeClassified, classifiedVis, 'before_classified'); +Map.addLayer(beforeClassified.clip(geometry), classifiedVis, 'before_classified'); // 2020 Jan var after = s2 .filter(ee.Filter.date('2020-01-01', '2020-02-01')) .filter(ee.Filter.bounds(geometry)) .select('B.*') - .median() - .clip(geometry); - -Map.addLayer(after, rgbVis, 'after'); - -// Classify the image. -var afterClassified= after.classify(classifier); -Map.addLayer(afterClassified, classifiedVis, 'after_classified'); + .median(); + +Map.addLayer(after.clip(geometry), rgbVis, 'after'); + +// Classify the image. +var afterClassified= after.classify(classifier); +Map.addLayer(afterClassified.clip(geometry), classifiedVis, 'after_classified'); + - -// Reclassify from 0-3 to 1-4 -var beforeClasses = beforeClassified.remap([0, 1, 2, 3], [1, 2, 3, 4]); -var afterClasses = afterClassified.remap([0, 1, 2, 3], [1, 2, 3, 4]); - -// Show all changed areas -var changed = afterClasses.subtract(beforeClasses).neq(0); -Map.addLayer(changed, {min:0, max:1, palette: ['white', 'red']}, 'Change'); - -// We multiply the before image with 100 and add the after image -// The resulting pixel values will be unique and will represent each unique transition -// i.e. 102 is urban to bare, 103 urban to water etc. -var merged = beforeClasses.multiply(100).add(afterClasses).rename('transitions'); - -// Use a frequencyHistogram to get a pixel count per class -var transitionMatrix = merged.reduceRegion({ - reducer: ee.Reducer.frequencyHistogram(), - geometry: geometry, - maxPixels: 1e10, - scale:10, - tileScale: 16 -}); -// This prints number of pixels for each class transition -print(transitionMatrix.get('transitions')); - -// If we want to calculate the area of each class transition -// we can use a grouped reducer - -// Divide by 1e6 to get the area in sq.km. -var areaImage = ee.Image.pixelArea().divide(1e6).addBands(merged); -// Calculate Area by each Transition Class -// using a Grouped Reducer -var areas = areaImage.reduceRegion({ - reducer: ee.Reducer.sum().group({ - groupField: 1, - groupName: 'transitions', - }), - geometry: geometry, - scale: 100, - tileScale: 4, - maxPixels: 1e10 - }); - -// Post-process the result to generate a clean output -var classAreas = ee.List(areas.get('groups')); -var classAreaLists = classAreas.map(function(item) { - var areaDict = ee.Dictionary(item); - var classNumber = ee.Number(areaDict.get('transitions')).format(); - var area = ee.Number(areaDict.get('sum')).round(); - return ee.List([classNumber, area]); - }); -var classTransitionsAreaDict = ee.Dictionary(classAreaLists.flatten()); -print(classTransitionsAreaDict);
    +// Reclassify from 0-3 to 1-4 +var beforeClasses = beforeClassified.remap([0, 1, 2, 3], [1, 2, 3, 4]); +var afterClasses = afterClassified.remap([0, 1, 2, 3], [1, 2, 3, 4]); + +// Show all changed areas +var changed = afterClasses.subtract(beforeClasses).neq(0); +Map.addLayer(changed.clip(geometry), {min:0, max:1, palette: ['white', 'red']}, 'Change'); + +// We multiply the before image with 100 and add the after image +// The resulting pixel values will be unique and will represent each unique transition +// i.e. 102 is urban to bare, 103 urban to water etc. +var merged = beforeClasses.multiply(100).add(afterClasses).rename('transitions'); + +// Use a frequencyHistogram to get a pixel count per class +var transitionMatrix = merged.reduceRegion({ + reducer: ee.Reducer.frequencyHistogram(), + geometry: geometry, + maxPixels: 1e10, + scale:10, + tileScale: 16 +}); +// This prints number of pixels for each class transition +print(transitionMatrix.get('transitions')); + +// If we want to calculate the area of each class transition +// we can use a grouped reducer + +// Divide by 1e6 to get the area in sq.km. +var areaImage = ee.Image.pixelArea().divide(1e6).addBands(merged); +// Calculate Area by each Transition Class +// using a Grouped Reducer +var areas = areaImage.reduceRegion({ + reducer: ee.Reducer.sum().group({ + groupField: 1, + groupName: 'transitions', + }), + geometry: geometry, + scale: 100, + tileScale: 4, + maxPixels: 1e10 + }); + +// Post-process the result to generate a clean output +var classAreas = ee.List(areas.get('groups')); +var classAreaLists = classAreas.map(function(item) { + var areaDict = ee.Dictionary(item); + var classNumber = ee.Number(areaDict.get('transitions')).format(); + var area = ee.Number(areaDict.get('sum')).round(); + return ee.List([classNumber, area]); + }); +var classTransitionsAreaDict = ee.Dictionary(classAreaLists.flatten()); +print(classTransitionsAreaDict);

    Exercise

    @@ -4237,7 +4237,7 @@

    05. Create a Split Panel App

    var geometry = selected.geometry(); Map.centerObject(geometry) -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED"); // Write a function for Cloud masking var maskS2clouds = function(image) { @@ -4320,7 +4320,7 @@

    Exercise

    var geometry = selected.geometry(); Map.centerObject(geometry) -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED"); // Write a function for Cloud masking var maskS2clouds = function(image) { diff --git a/end-to-end-gee-supplement.Rmd b/end-to-end-gee-supplement.Rmd index f7d48779..f2294c43 100644 --- a/end-to-end-gee-supplement.Rmd +++ b/end-to-end-gee-supplement.Rmd @@ -268,6 +268,17 @@ During the [Israel-Palestine Crisis of 2021](https://en.wikipedia.org/wiki/2021_ ```{js eval=FALSE, code=readLines('code/end_to_end_gee/Supplement/Image_Collections/Get_Pixelwise_Dates_in_Composites')} ``` +## Filter Images by Cloud Cover in a Region + +This script shows how to calculate the cloud cover in a region, and set an image property with the cloud cover for a given region. We can then apply a filter to select images having no cloud cover in the region. This is useful where you are working in a very cloudy region and want to ensure that you are filtering for clouds in your region of interest, instead of the whole scene. + +[Open in Code Editor ↗](https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3ASupplement%2FImage_Collections%2FFilter_by_Cloud_Cover_In_Region){target="_blank"} + +```{js eval=FALSE, code=readLines('code/end_to_end_gee/Supplement/Image_Collections/Filter_by_Cloud_Cover_In_Region')} +``` + + + ## Harmonized Landsat Time Series [Open in Code Editor ↗](https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3ASupplement%2FImage_Collections%2FHarmonized_Landsat_Time_Series){target="_blank"}