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'