From 2196878a058eb0e0a3ea311de3137dabb84a4ad4 Mon Sep 17 00:00:00 2001 From: Ujaval Gandhi Date: Fri, 1 Sep 2023 15:44:12 +0530 Subject: [PATCH] update --- ...xporting_Classification_Results_(exercise) | 6 + .../04a_Post_Classification_Comparison | 2 +- ..._Post_Classification_Comparison_(exercise) | 2 +- .../Gaza_Conflict_Change_Detection_Angle | 2 +- .../Gaza_Conflict_Change_Detection_Index | 2 +- .../Exporting_ImageCollections | 2 +- .../Sentinel2_Cloud_Probability_Mask | 4 +- .../UI_Widgets_and_Apps/Colorbar_Legend | 4 +- .../UI_Widgets_and_Apps/NDVI Explorer UI App | 85 +++++-- ...xporting_Classification_Results_(exercise) | 6 + .../04a_Post_Classification_Comparison | 2 +- ..._Post_Classification_Comparison_(exercise) | 2 +- .../Gaza_Conflict_Change_Detection_Angle | 2 +- .../Gaza_Conflict_Change_Detection_Index | 2 +- .../Exporting_ImageCollections | 2 +- .../Sentinel2_Cloud_Probability_Mask | 4 +- .../UI_Widgets_and_Apps/Colorbar_Legend | 4 +- .../UI_Widgets_and_Apps/NDVI Explorer UI App | 85 +++++-- docs/end-to-end-gee-supplement.html | 237 +++++++++++------- docs/end-to-end-gee.html | 7 +- 20 files changed, 310 insertions(+), 152 deletions(-) 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 37fa0767..973a8b8f 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) @@ -143,3 +143,9 @@ Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '201 // The pyramidingPolicy parameter should a dictionary specifying // the policy for each band. A simpler way to specify it for all // bands is to use {'.default': 'mode'} + +// assetId should be specified as a string +// Lookup your asset root name from the 'Assets' tab +// If it is 'users/username', you can specify the id as +// 'users/username/classified_image' + 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 3c29b017..b49545d7 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 @@ -283,7 +283,7 @@ var after = s2 .select('B.*') .median(); -Map.addLayer(after, rgbVis, 'after'); +Map.addLayer(after.clip(geometry), rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); 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 1510b37f..f08bcb36 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) @@ -58,7 +58,7 @@ var after = s2 .select('B.*') .median(); -Map.addLayer(after, rgbVis, 'after'); +Map.addLayer(after.clip(geometry), rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); diff --git a/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Angle b/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Angle index f56fa3ed..ba54c3c2 100644 --- a/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Angle +++ b/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Angle @@ -26,7 +26,7 @@ function maskS2clouds(image) { } -var s2 = ee.ImageCollection('COPERNICUS/S2') +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED') .filter(ee.Filter.bounds(geometry)) .map(maskS2clouds); diff --git a/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Index b/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Index index 5829d9ca..0dbb2cda 100644 --- a/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Index +++ b/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Index @@ -23,7 +23,7 @@ function maskS2clouds(image) { } -var s2 = ee.ImageCollection("COPERNICUS/S2") +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED") .filter(ee.Filter.bounds(geometry)) .map(maskS2clouds); diff --git a/code/end_to_end_gee/Supplement/Image_Collections/Exporting_ImageCollections b/code/end_to_end_gee/Supplement/Image_Collections/Exporting_ImageCollections index 40d75057..bb2bc249 100644 --- a/code/end_to_end_gee/Supplement/Image_Collections/Exporting_ImageCollections +++ b/code/end_to_end_gee/Supplement/Image_Collections/Exporting_ImageCollections @@ -8,7 +8,7 @@ // We create a NDVI time-series and export each // image as a separate GeoTiff file -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED/"); var geometry = ee.Geometry.Polygon([[ [82.60642647743225, 27.16350437805251], [82.60984897613525, 27.1618529901377], diff --git a/code/end_to_end_gee/Supplement/Image_Processing/Sentinel2_Cloud_Probability_Mask b/code/end_to_end_gee/Supplement/Image_Processing/Sentinel2_Cloud_Probability_Mask index 02c653dd..c0543b2e 100644 --- a/code/end_to_end_gee/Supplement/Image_Processing/Sentinel2_Cloud_Probability_Mask +++ b/code/end_to_end_gee/Supplement/Image_Processing/Sentinel2_Cloud_Probability_Mask @@ -11,9 +11,9 @@ var imageId = '20190703T050701_20190703T052312_T43PGP'; // S2 Cloudless Algorithm // This algorithm requires different bands from 3 different datasets -var s2Bands = ee.Image('COPERNICUS/S2/'+ imageId) +var s2Bands = ee.Image('COPERNICUS/S2_HARMONIZED/'+ imageId) .select(['B7', 'B8', 'B8A', 'B10']); -var s2SrBands = ee.Image('COPERNICUS/S2_SR/' + imageId) +var s2SrBands = ee.Image('COPERNICUS/S2_SR_HARMONIZED/' + imageId) .select(['B2', 'B3', 'B4', 'B5']); var s2CloudBands = ee.Image('COPERNICUS/S2_CLOUD_PROBABILITY/'+ imageId) .select(['probability']) diff --git a/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/Colorbar_Legend b/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/Colorbar_Legend index 32d20467..7a12edb5 100644 --- a/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/Colorbar_Legend +++ b/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/Colorbar_Legend @@ -1,5 +1,5 @@ -var s2 = ee.ImageCollection("COPERNICUS/S2"); -var admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2"); +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED'); +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() diff --git a/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/NDVI Explorer UI App b/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/NDVI Explorer UI App index 4b63acfb..5282e29e 100644 --- a/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/NDVI Explorer UI App +++ b/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/NDVI Explorer UI App @@ -1,7 +1,7 @@ var geometry = ee.Geometry.Point([77.5979, 13.00896]); Map.centerObject(geometry, 10) -var s2 = ee.ImageCollection("COPERNICUS/S2") +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED') var rgbVis = { min: 0.0, max: 0.3, @@ -24,8 +24,8 @@ function maskS2clouds(image) { 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"]) + .select('B.*') + .copyProperties(image, ['system:time_start']) } @@ -60,6 +60,7 @@ var chartPanel = ui.Panel(); var selectionPanel = ui.Panel({ layout: ui.Panel.Layout.flow('horizontal'), }); +var downloadPanel = ui.Panel(); resultsPanel.style().set({ width: '400px', @@ -69,15 +70,17 @@ resultsPanel.style().set({ var resetPanel = ui.Panel(); -resultsPanel.add(selectionPanel) -resultsPanel.add(chartPanel) -resultsPanel.add(resetPanel) +resultsPanel.add(selectionPanel); +resultsPanel.add(chartPanel); +resultsPanel.add(downloadPanel); +resultsPanel.add(resetPanel); // Function to reset the app to initial state var resetEverything = function() { - chartPanel.clear() - selectionPanel.clear() - resetPanel.clear() + chartPanel.clear(); + selectionPanel.clear(); + downloadPanel.clear(); + resetPanel.clear(); Map.clear() @@ -104,7 +107,7 @@ var displayChart = function(point) { var filtered = s2 .filter(ee.Filter.date('2019-01-01', '2019-12-31')) - .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 50)) + .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30)) .map(maskS2clouds) .map(addNDVI) .filter(ee.Filter.bounds(geometry)) @@ -116,7 +119,10 @@ var displayChart = function(point) { scale: 20}).setOptions({ title: 'NDVI Time Series', vAxis: {title: 'NDVI'}, - hAxis: {title: 'Date', gridlines: {count: 12}} + hAxis: {title: 'Date', gridlines: {count: 12}}, + interpolateNulls: true, + pointSize: 2, + lineWidth: 1 }) chartPanel.clear() @@ -124,27 +130,68 @@ var displayChart = function(point) { selectionPanel.add(ui.Label('Choose an image to display:')) chartPanel.add(chart) + // S2 collection has overlapping granules for same dates + // Add a 'date' property so we can merge data for the same date + var filtered = filtered.map(function(image) { + var dateString = ee.Date(image.date()).format('YYYY-MM-dd') + return image.set('date', dateString); + }); 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()) + var 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){ + var dates = filtered.aggregate_array('date').distinct(); + + // Add dates to a dropdown selector + dates.evaluate(function(dateList){ selectionPanel.add(ui.Select({ items: dateList, onChange: addNdviLayer, placeholder: 'Select a date' })) }) + + // Extract the NDVI values as a FeatureCollection + var ndviFc = ee.FeatureCollection(dates.map(function(dateString) { + var date = ee.Date.parse('YYYY-MM-dd', dateString) + var image = filtered + .filter(ee.Filter.date(date, date.advance(1, 'day'))) + .mosaic(); + + var ndviImage = image.select('ndvi'); + var stats = ndviImage.reduceRegion({ + reducer: ee.Reducer.mean().setOutputs(['ndvi']), + geometry: geometry, + scale: 20 + }); + // Add date as wel as lat/lon columns + var properties = stats.combine({ + 'date': dateString, + 'longitude': point['lon'], + 'latitude': point['lat'] + }) + return ee.Feature(null, properties); + })); + + // Prepare the collection to download + var downloadReady = function(url) { + var label = ui.Label({ + value: 'Download CSV', + targetUrl: url}) + downloadPanel.add(label); + } + ndviFc.getDownloadURL({ + format: 'CSV', + selectors: ['date', 'latitude', 'longitude', 'ndvi'], + filename: 'ndvi_time_series', + callback: downloadReady}) -}); +//}); } // Call the function to build the initial UI state. 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 37fa0767..973a8b8f 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) @@ -143,3 +143,9 @@ Map.addLayer(classified.clip(geometry), {min: 0, max: 3, palette: palette}, '201 // The pyramidingPolicy parameter should a dictionary specifying // the policy for each band. A simpler way to specify it for all // bands is to use {'.default': 'mode'} + +// assetId should be specified as a string +// Lookup your asset root name from the 'Assets' tab +// If it is 'users/username', you can specify the id as +// 'users/username/classified_image' + 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 3c29b017..b49545d7 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 @@ -283,7 +283,7 @@ var after = s2 .select('B.*') .median(); -Map.addLayer(after, rgbVis, 'after'); +Map.addLayer(after.clip(geometry), rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); 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 1510b37f..f08bcb36 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) @@ -58,7 +58,7 @@ var after = s2 .select('B.*') .median(); -Map.addLayer(after, rgbVis, 'after'); +Map.addLayer(after.clip(geometry), rgbVis, 'after'); // Classify the image. var afterClassified= after.classify(classifier); diff --git a/docs/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Angle b/docs/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Angle index f56fa3ed..ba54c3c2 100644 --- a/docs/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Angle +++ b/docs/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Angle @@ -26,7 +26,7 @@ function maskS2clouds(image) { } -var s2 = ee.ImageCollection('COPERNICUS/S2') +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED') .filter(ee.Filter.bounds(geometry)) .map(maskS2clouds); diff --git a/docs/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Index b/docs/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Index index 5829d9ca..0dbb2cda 100644 --- a/docs/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Index +++ b/docs/code/end_to_end_gee/Supplement/Change_Detection/Gaza_Conflict_Change_Detection_Index @@ -23,7 +23,7 @@ function maskS2clouds(image) { } -var s2 = ee.ImageCollection("COPERNICUS/S2") +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED") .filter(ee.Filter.bounds(geometry)) .map(maskS2clouds); diff --git a/docs/code/end_to_end_gee/Supplement/Image_Collections/Exporting_ImageCollections b/docs/code/end_to_end_gee/Supplement/Image_Collections/Exporting_ImageCollections index 40d75057..bb2bc249 100644 --- a/docs/code/end_to_end_gee/Supplement/Image_Collections/Exporting_ImageCollections +++ b/docs/code/end_to_end_gee/Supplement/Image_Collections/Exporting_ImageCollections @@ -8,7 +8,7 @@ // We create a NDVI time-series and export each // image as a separate GeoTiff file -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED/"); var geometry = ee.Geometry.Polygon([[ [82.60642647743225, 27.16350437805251], [82.60984897613525, 27.1618529901377], diff --git a/docs/code/end_to_end_gee/Supplement/Image_Processing/Sentinel2_Cloud_Probability_Mask b/docs/code/end_to_end_gee/Supplement/Image_Processing/Sentinel2_Cloud_Probability_Mask index 02c653dd..c0543b2e 100644 --- a/docs/code/end_to_end_gee/Supplement/Image_Processing/Sentinel2_Cloud_Probability_Mask +++ b/docs/code/end_to_end_gee/Supplement/Image_Processing/Sentinel2_Cloud_Probability_Mask @@ -11,9 +11,9 @@ var imageId = '20190703T050701_20190703T052312_T43PGP'; // S2 Cloudless Algorithm // This algorithm requires different bands from 3 different datasets -var s2Bands = ee.Image('COPERNICUS/S2/'+ imageId) +var s2Bands = ee.Image('COPERNICUS/S2_HARMONIZED/'+ imageId) .select(['B7', 'B8', 'B8A', 'B10']); -var s2SrBands = ee.Image('COPERNICUS/S2_SR/' + imageId) +var s2SrBands = ee.Image('COPERNICUS/S2_SR_HARMONIZED/' + imageId) .select(['B2', 'B3', 'B4', 'B5']); var s2CloudBands = ee.Image('COPERNICUS/S2_CLOUD_PROBABILITY/'+ imageId) .select(['probability']) diff --git a/docs/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/Colorbar_Legend b/docs/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/Colorbar_Legend index 32d20467..7a12edb5 100644 --- a/docs/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/Colorbar_Legend +++ b/docs/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/Colorbar_Legend @@ -1,5 +1,5 @@ -var s2 = ee.ImageCollection("COPERNICUS/S2"); -var admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2"); +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED'); +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() diff --git a/docs/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/NDVI Explorer UI App b/docs/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/NDVI Explorer UI App index 4b63acfb..5282e29e 100644 --- a/docs/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/NDVI Explorer UI App +++ b/docs/code/end_to_end_gee/Supplement/UI_Widgets_and_Apps/NDVI Explorer UI App @@ -1,7 +1,7 @@ var geometry = ee.Geometry.Point([77.5979, 13.00896]); Map.centerObject(geometry, 10) -var s2 = ee.ImageCollection("COPERNICUS/S2") +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED') var rgbVis = { min: 0.0, max: 0.3, @@ -24,8 +24,8 @@ function maskS2clouds(image) { 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"]) + .select('B.*') + .copyProperties(image, ['system:time_start']) } @@ -60,6 +60,7 @@ var chartPanel = ui.Panel(); var selectionPanel = ui.Panel({ layout: ui.Panel.Layout.flow('horizontal'), }); +var downloadPanel = ui.Panel(); resultsPanel.style().set({ width: '400px', @@ -69,15 +70,17 @@ resultsPanel.style().set({ var resetPanel = ui.Panel(); -resultsPanel.add(selectionPanel) -resultsPanel.add(chartPanel) -resultsPanel.add(resetPanel) +resultsPanel.add(selectionPanel); +resultsPanel.add(chartPanel); +resultsPanel.add(downloadPanel); +resultsPanel.add(resetPanel); // Function to reset the app to initial state var resetEverything = function() { - chartPanel.clear() - selectionPanel.clear() - resetPanel.clear() + chartPanel.clear(); + selectionPanel.clear(); + downloadPanel.clear(); + resetPanel.clear(); Map.clear() @@ -104,7 +107,7 @@ var displayChart = function(point) { var filtered = s2 .filter(ee.Filter.date('2019-01-01', '2019-12-31')) - .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 50)) + .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30)) .map(maskS2clouds) .map(addNDVI) .filter(ee.Filter.bounds(geometry)) @@ -116,7 +119,10 @@ var displayChart = function(point) { scale: 20}).setOptions({ title: 'NDVI Time Series', vAxis: {title: 'NDVI'}, - hAxis: {title: 'Date', gridlines: {count: 12}} + hAxis: {title: 'Date', gridlines: {count: 12}}, + interpolateNulls: true, + pointSize: 2, + lineWidth: 1 }) chartPanel.clear() @@ -124,27 +130,68 @@ var displayChart = function(point) { selectionPanel.add(ui.Label('Choose an image to display:')) chartPanel.add(chart) + // S2 collection has overlapping granules for same dates + // Add a 'date' property so we can merge data for the same date + var filtered = filtered.map(function(image) { + var dateString = ee.Date(image.date()).format('YYYY-MM-dd') + return image.set('date', dateString); + }); 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()) + var 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){ + var dates = filtered.aggregate_array('date').distinct(); + + // Add dates to a dropdown selector + dates.evaluate(function(dateList){ selectionPanel.add(ui.Select({ items: dateList, onChange: addNdviLayer, placeholder: 'Select a date' })) }) + + // Extract the NDVI values as a FeatureCollection + var ndviFc = ee.FeatureCollection(dates.map(function(dateString) { + var date = ee.Date.parse('YYYY-MM-dd', dateString) + var image = filtered + .filter(ee.Filter.date(date, date.advance(1, 'day'))) + .mosaic(); + + var ndviImage = image.select('ndvi'); + var stats = ndviImage.reduceRegion({ + reducer: ee.Reducer.mean().setOutputs(['ndvi']), + geometry: geometry, + scale: 20 + }); + // Add date as wel as lat/lon columns + var properties = stats.combine({ + 'date': dateString, + 'longitude': point['lon'], + 'latitude': point['lat'] + }) + return ee.Feature(null, properties); + })); + + // Prepare the collection to download + var downloadReady = function(url) { + var label = ui.Label({ + value: 'Download CSV', + targetUrl: url}) + downloadPanel.add(label); + } + ndviFc.getDownloadURL({ + format: 'CSV', + selectors: ['date', 'latitude', 'longitude', 'ndvi'], + filename: 'ndvi_time_series', + callback: downloadReady}) -}); +//}); } // Call the function to build the initial UI state. diff --git a/docs/end-to-end-gee-supplement.html b/docs/end-to-end-gee-supplement.html index 3707afa8..329cf338 100644 --- a/docs/end-to-end-gee-supplement.html +++ b/docs/end-to-end-gee-supplement.html @@ -2637,7 +2637,7 @@

Conflict Mapping

} -var s2 = ee.ImageCollection("COPERNICUS/S2") +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED") .filter(ee.Filter.bounds(geometry)) .map(maskS2clouds); @@ -2849,7 +2849,7 @@

Exporting ImageCollections

// We create a NDVI time-series and export each // image as a separate GeoTiff file -var s2 = ee.ImageCollection("COPERNICUS/S2"); +var s2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED/"); var geometry = ee.Geometry.Polygon([[ [82.60642647743225, 27.16350437805251], [82.60984897613525, 27.1618529901377], @@ -4565,8 +4565,8 @@

Adding a Continous Legend

href="https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3ASupplement%2FUI_Widgets_and_Apps%2FColorbar_Legend" target="_blank">Open in Code Editor ↗

var s2 = ee.ImageCollection("COPERNICUS/S2");
-var admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2");
+class="sourceCode js">var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
+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()
@@ -4707,7 +4707,7 @@ 

NDVI Explorer UI App

class="sourceCode js">var geometry = ee.Geometry.Point([77.5979, 13.00896]); Map.centerObject(geometry, 10) -var s2 = ee.ImageCollection("COPERNICUS/S2") +var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED') var rgbVis = { min: 0.0, max: 0.3, @@ -4730,8 +4730,8 @@

NDVI Explorer UI App

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"]) + .select('B.*') + .copyProperties(image, ['system:time_start']) } @@ -4766,95 +4766,142 @@

NDVI Explorer UI App

var selectionPanel = ui.Panel({ layout: ui.Panel.Layout.flow('horizontal'), }); - -resultsPanel.style().set({ - width: '400px', - position: 'bottom-right' -}); - -var resetPanel = ui.Panel(); - +var downloadPanel = ui.Panel(); + +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();
+ +resultsPanel.add(selectionPanel); +resultsPanel.add(chartPanel); +resultsPanel.add(downloadPanel); +resultsPanel.add(resetPanel); + +// Function to reset the app to initial state +var resetEverything = function() { + chartPanel.clear(); + selectionPanel.clear(); + downloadPanel.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', 30)) + .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}}, + interpolateNulls: true, + pointSize: 2, + lineWidth: 1 + }) + + chartPanel.clear() + selectionPanel.clear() + selectionPanel.add(ui.Label('Choose an image to display:')) + chartPanel.add(chart) + + // S2 collection has overlapping granules for same dates + // Add a 'date' property so we can merge data for the same date + var filtered = filtered.map(function(image) { + var dateString = ee.Date(image.date()).format('YYYY-MM-dd') + return image.set('date', dateString); + }); + + var addNdviLayer = function(dateString) { + var date = ee.Date.parse('YYYY-MM-dd', dateString) + var image = filtered + .filter(ee.Filter.date(date, date.advance(1, 'day'))) + .mosaic(); + Map.addLayer(image.select('ndvi'), ndviVis, 'NDVI Image -' + dateString) + } + + var dates = filtered.aggregate_array('date').distinct(); + + // Add dates to a dropdown selector + dates.evaluate(function(dateList){ + selectionPanel.add(ui.Select({ + items: dateList, + onChange: addNdviLayer, + placeholder: 'Select a date' + })) + }) + + // Extract the NDVI values as a FeatureCollection + var ndviFc = ee.FeatureCollection(dates.map(function(dateString) { + var date = ee.Date.parse('YYYY-MM-dd', dateString) + var image = filtered + .filter(ee.Filter.date(date, date.advance(1, 'day'))) + .mosaic(); + + var ndviImage = image.select('ndvi'); + var stats = ndviImage.reduceRegion({ + reducer: ee.Reducer.mean().setOutputs(['ndvi']), + geometry: geometry, + scale: 20 + }); + // Add date as wel as lat/lon columns + var properties = stats.combine({ + 'date': dateString, + 'longitude': point['lon'], + 'latitude': point['lat'] + }) + return ee.Feature(null, properties); + })); + + // Prepare the collection to download + var downloadReady = function(url) { + var label = ui.Label({ + value: 'Download CSV', + targetUrl: url}) + downloadPanel.add(label); + } + ndviFc.getDownloadURL({ + format: 'CSV', + selectors: ['date', 'latitude', 'longitude', 'ndvi'], + filename: 'ndvi_time_series', + callback: downloadReady}) + +//}); + +} +// Call the function to build the initial UI state. +resetEverything();
diff --git a/docs/end-to-end-gee.html b/docs/end-to-end-gee.html index 5d47d0b2..de07e36e 100644 --- a/docs/end-to-end-gee.html +++ b/docs/end-to-end-gee.html @@ -3030,7 +3030,12 @@

Exercise

// pyramidingPolicy to 'mode'. // The pyramidingPolicy parameter should a dictionary specifying // the policy for each band. A simpler way to specify it for all -// bands is to use {'.default': 'mode'}
+// bands is to use {'.default': 'mode'} + +// assetId should be specified as a string +// Lookup your asset root name from the 'Assets' tab +// If it is 'users/username', you can specify the id as +// 'users/username/classified_image'