added ws202425 courses

This commit is contained in:
2024-11-14 13:11:04 +01:00
parent 800282c2e8
commit beb31897b1
461 changed files with 20368 additions and 0 deletions
@@ -0,0 +1,40 @@
////// Satellite images and RGB color presentation //////
/// Open a satellite image and create an RGB-Composite
// Load an image.
var image = ee.Image('LANDSAT/LC08/C01/T1_TOA/LC08_044034_20140318');
// Define the visualization parameters.
var visParams = {
bands: ['B5', 'B4', 'B3'],
min: 0,
max: 0.5,
};
// Center the map and set zoom level
Map.setCenter(-122.1899, 37.5010, 10); // San Francisco Bay
// Visualize image
Map.addLayer(image, visParams, 'false color composite');
//// Exercise 1a:
// Play around with the visualization
//// Clip the image to a feature:
// Create a circle by drawing a 20000 meter buffer around a point.
//var roi = ee.Geometry.Point([-122.4481, 37.7599]).buffer(20000);
// Display a clipped version of the mosaic.
// After the layers are visible in the Map view, please do only display the layer "buffer"
//Map.addLayer(image.clip(roi), visParams, 'buffer');
//// Exercise 1b:
// Create a polygon and clip the image to this feature
// 1. Create a polygon with the available tool (freehand or rectangle)
// 2. You will find the feature under imports at the beginning of your script
// 3. Change the function image.clip accordingly
// 4. Re-run the sript
Map.addLayer(image.clip(Rechteck), visParams, 'Rechteck')
@@ -0,0 +1,22 @@
// Open SRTM image and visualize it
// Detailed description: https://developers.google.com/earth-engine/tutorial_api_02
// Instantiate an image with the Image constructor.
var image = ee.Image('CGIAR/SRTM90_V4');
// Zoom to a location.
Map.setCenter(12.978717025594149,47.55544193154224,11); // Center on Königssee - Berchtesgaden.
// datatype
print('SRTM image', image);
// Display the image on the map.
Map.addLayer(image);
// Customizing layer visualization
Map.addLayer(image, {min: 0, max: 3000}, 'custom visualization');
// Customizing layer visualization - color palette
Map.addLayer(image, {min: 0, max: 3000, palette: ['blue', 'green', 'red']},
'custom palette');
@@ -0,0 +1,65 @@
////// Script 2: Calculate indices and apply a mask //////
// Load an image.
var image = ee.Image('LANDSAT/LC08/C01/T1_TOA/LC08_044034_20140318');
// Center map view
Map.centerObject(image,10)
// Create an NDWI image, define visualization parameters and display.
var ndwi = image.normalizedDifference(['B3', 'B5']);
var ndwiViz = {min: 0.5, max: 1, palette: ['00FFFF', '0000FF']};
Map.addLayer(ndwi, ndwiViz, 'NDWI');
Map.addLayer(ndwi, ndwiViz, 'NDWI false', false);
// Question 1: What's "false" doing?
//Zeigt den Layer nicht an.
// Mask the non-watery parts of the image, where NDWI < 0.4.
var ndwiMasked = ndwi.updateMask(ndwi.gte(0.4));
Map.addLayer(ndwiMasked, ndwiViz, 'NDWI masked');
// Question 2: What does the function .gte actually do?
//Maskiert alles wo der NDWI unter 0.4 ist.
//// Create visualization layers.
// The .visualize function creates images that contain the visualisation parameters.
var imageRGB = image.visualize({bands: ['B3', 'B2', 'B1'], max: 0.5});
var ndwiRGB = ndwiMasked.visualize({min: 0.5, max: 1, palette: ['00FFFF', '0000FF']});
Map.addLayer(imageRGB, {}, 'imageRGB');
Map.addLayer(ndwiRGB, {}, 'NDWI masked - 2'); //looks the same than the fist visualization, just another layer
// Mosaic the visualization layers and display.
var mosaic = ee.ImageCollection([imageRGB, ndwiRGB]).mosaic();
Map.addLayer(mosaic, {}, 'mosaic');
//Question 3: What does the mosaic function do?
//Fügt das RGB Image und das NDWI Image zusammen.
// Exercise 2a:
// Calculate the NDVI and visualize it
var ndvi = image.normalizedDifference(['B4', 'B5']);
// Find a threshold for differentiating vegetated and non-vegetated areas
var ndviViz = {min: -1, max: 1};
Map.addLayer(ndvi, ndviViz, 'NDVI');
// Create a mask that masks non-vegetated areas
// Mask the non-planty parts of the image, where NDVI < 0.
var ndviMasked = ndvi.updateMask(ndvi.gte(0));
// Find a nice visualization
var ndviRGB = ndviMasked.visualize({min: -1, max: 1, palette: ['00FFFF', '0000FF']});
Map.addLayer(ndviRGB, {}, 'NDVI masked');
// Create a mosaic of the Landsat image and the NDVI data.
var mosaic = ee.ImageCollection([imageRGB, ndviRGB]).mosaic();
Map.addLayer(mosaic, {}, 'mosaic');
@@ -0,0 +1,48 @@
// Calculate slope and aspect
// Calculate statistics
//Detailed description: https://developers.google.com/earth-engine/tutorial_api_03
// Load the SRTM image.
var srtm = ee.Image('CGIAR/SRTM90_V4');
// Apply an algorithm to an image.
var slope = ee.Terrain.slope(srtm);
print(slope)
// Convert degree in percent
// Display the result.
Map.setCenter(12.978717025594149,47.55544193154224,11); // Center on Königsee.
Map.addLayer(srtm, {min: 0, max :2500}, 'srtm');
Map.addLayer(slope, {min: 0, max :60}, 'slope');
//Image math
// Get the aspect (in degrees).
var aspect = ee.Terrain.aspect(srtm);
// Convert to radians, compute the sin of the aspect.
var sinImage = aspect.divide(180).multiply(Math.PI).sin();
// Display the result.
Map.addLayer(sinImage, {min: -1, max: 1}, 'sin');
// Image statistics
// Compute the mean elevation in the polygon.
var stddevDict = srtm.reduceRegion({
reducer: ee.Reducer.stdDev(),
geometry: koenigsee,
scale: 90
});
// Print dictionary
print("dict", stddevDict)
// Get the mean from the dictionary and print it.
// In java a dictionary is a collection of different kinds of information.
// In other languages such as R a dictionary is usally named 'object'.
var stddev = stddevDict.get('elevation');
print('stdDev elevation', stddev);
@@ -0,0 +1,82 @@
////// Skript 3: First Image Collection ///////
//// Open an image collection
// An image collection refers to a set of Earth Engine images. For example, the collection of all Landsat 8
// surface reflectance images is an ee.ImageCollection.
var l8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
print('Landsat 8 collection', l8);
// Question 1: Which error occurs when printing the contents of the variable l8?
// Before the second run, comment the line "print('Landsat 8 collection', l8);"
// No worries: we can still work with all the images…
//// Filter the collection
// The way to limit the collection by time or space is by filtering it.
// For example, to filter the collection to images that cover a particular location,
// first define your area of interest with a point (or line or polygon) using the geometry drawing tools.
var spatialFiltered = l8.filterBounds(point);
print('spatialFiltered', spatialFiltered);
var temporalFiltered = spatialFiltered.filterDate('2015-01-01', '2015-12-31');
print('temporalFiltered', temporalFiltered);
//// Exercise 3a:
// - Create a point somewhere and name it “point”. Of course you might give it another name,
// but then you need to change the code accordingly.
// - Have a look at another time period.
//// How to work with the collection
// Alternative 1: Visualize the Image collection as is
// Note: You may directly visualize the image collection but then only the newest one will be
// visualized (if you do not specify it otherwise)
var visParams = {bands: ['SR_B4', 'SR_B3', 'SR_B2'], min: 0, max: 30000};
Map.centerObject(temporalFiltered, 9);
Map.addLayer(temporalFiltered, visParams, 'l8 collection');
//// Alternative 2: How to use single images from the stack
// We may see and use the images that the collection contains.
var listofimages = temporalFiltered.toList(temporalFiltered.size());
print('List of images', listofimages);
// We may then assign the images to a variable. We start counting with 0.
var firstimage = listofimages.get(0);
var tenthimage = listofimages.get(9);
var lastimage = listofimages.get(listofimages.length().subtract(1));
print('tenth image', tenthimage);
// Visualize the 10th image
Map.addLayer(ee.Image(tenthimage), visParams, '10th image');
//// Alternative 3: Use the least cloudy image
// This will sort from least to most cloudy.
var sorted = temporalFiltered.sort('CLOUD_COVER');
print("sorted", sorted)
// Get the first (least cloudy) image.
var scene = sorted.first();
// Visualize the least cloudy image print("cloudless", scene)
Map.addLayer(scene, visParams, 'Least cloudy image');
//// Exercise 3a:
// - Use the Landsat 5 collection (surface reflectance).
// - Which images are available from the year of your birthday and your place of birth?
// - Are there any images from your month of birth?
// - Visualize the image that comes closest to your birthday
// - Plot the least cloudy image
// Of course you may use any other event. In this case you need to see if the Landsat 5 collection
// is the right one to use.
@@ -0,0 +1,32 @@
//// Script 4 - Additional Script Metadata
// Load an image.
var image = ee.Image('LANDSAT/LC08/C01/T1/LC08_044034_20140318');
// Get a list of all metadata properties.
var properties = image.propertyNames();
print('Metadata properties: ', properties); // ee.List of metadata properties
// Get information about the bands as a list.
var bandNames = image.bandNames();
print('Band names: ', bandNames); // ee.List of band names
// Get projection information from band 1.
var b1proj = image.select('B1').projection();
print('Band 1 projection: ', b1proj); // ee.Projection object
// Get scale (in meters) information from band 1.
var b1scale = image.select('B1').projection().nominalScale();
print('Band 1 scale: ', b1scale); // ee.Number
// Note that different bands can have different projections and scale.
var b8scale = image.select('B8').projection().nominalScale();
print('Band 8 scale: ', b8scale); // ee.Number
// Get a specific metadata property.
var cloudiness = image.get('CLOUD_COVER');
print('CLOUD_COVER: ', cloudiness); // ee.Number
// Get the timestamp and convert it to a date.
var date = ee.Date(image.get('system:time_start'));
print('Timestamp: ', date); // ee.Date
@@ -0,0 +1,32 @@
// Assemble a collection of Sentinel-2 surface reflectance images for a given
// region and date range.
var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
.filterBounds(ee.Geometry.Point(-96.9037, 48.0395))
.filterDate('2019-06-01', '2019-10-01');
// Define visualization arguments.
var visArgs = {bands: ['B11', 'B8', 'B3'], min: 300, max: 3500};
// Define a function to convert an image to an RGB visualization image and copy
// properties from the original image to the RGB image.
var visFun = function(img) {
return img.visualize(visArgs).copyProperties(img, img.propertyNames());
};
// Map over the image collection to convert each image to an RGB visualization
// using the previously defined visualization function.
var s2colVis = s2col.map(visFun);
print("s2col", s2colVis)
// Print the animation to the console as a ui.Thumbnail using the above defined
// arguments. Note that ui.Thumbnail produces an animation when the first input
// is an ee.ImageCollection instead of an ee.Image.
print(ui.Thumbnail(s2colVis, visFun));
// Zeigt den Median von allen Bildern
var s2col_comp = s2col.median()
Map.setCenter(-96.9037, 48.0395, 10)
Map.addLayer(s2col_comp, visArgs, "composite")
@@ -0,0 +1,29 @@
// Metadata of image collections
// Load a Landsat 8 ImageCollection for a single path-row.
var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA')
.filter(ee.Filter.eq('WRS_PATH', 44))
.filter(ee.Filter.eq('WRS_ROW', 34))
.filterDate('2014-03-01', '2014-08-01');
print('Collection: ', collection);
// Get the number of images.
var count = collection.size();
print('Count: ', count);
// Get the date range of images in the collection.
var range = collection.reduceColumns(ee.Reducer.minMax(), ["system:time_start"])
print('Date range: ', ee.Date(range.get('min')), ee.Date(range.get('max')))
// Get statistics for a property of the images in the collection.
var sunStats = collection.aggregate_stats('SUN_ELEVATION');
print('Sun elevation statistics: ', sunStats);
// Sort by a cloud cover property, get the least cloudy image.
var image = ee.Image(collection.sort('CLOUD_COVER').first());
print('Least cloudy image: ', image);
// Limit the collection to the 10 most recent images.
var recent = collection.sort('system:time_start', false).limit(10);
print('Recent images: ', recent);
@@ -0,0 +1,25 @@
// Filter and first composites
// Load Landsat 5 data, filter by date and bounds.
var collection = ee.ImageCollection('LANDSAT/LT05/C01/T1')
.filterDate('1987-01-01', '1989-12-31')
.filter(ee.Filter.calendarRange(6,8,'month'))
.filterBounds(ee.Geometry.Point(13.41, 52.53));
// Also filter the collection by the CLOUD_COVER property.
var filtered = collection
.filterMetadata('CLOUD_COVER', 'equals', 0);
// Create two composites to check the effect of filtering by CLOUD_COVER.
var unfilteredComposite = collection.mean()
var cloudfreeComposite = filtered.mean()
// Display the composites.
Map.setCenter(13.41, 52.53, 13);
Map.addLayer(unfilteredComposite,
{bands: ['B4', 'B3', 'B2'], gain: 3.5},
'unfiltered composite');
Map.addLayer(cloudfreeComposite,
{bands: ['B4', 'B3', 'B2'], gain: 3.5},
'cloudless composite');
@@ -0,0 +1,15 @@
// Simple composite
var landsat = ee.ImageCollection("LANDSAT/LC08/C01/T1")
.filterDate('2016-01-01', '2017-01-01')
.filterBounds(geometry)
Map.addLayer(landsat, {bands: ["B4", "B3", "B2"], min:0, max: 30000})
var composite = ee.Algorithms.Landsat.simpleComposite({
collection: landsat,
asFloat: true
})
Map.addLayer(composite, {bands: ["B4", "B3", "B2"], min:0, max: 0.3})
Map.centerObject(geometry, 11)
@@ -0,0 +1,36 @@
// Landsat 8 - Cloud masking
// This example demonstrates the use of the pixel QA band to mask
// clouds in surface reflectance (SR) data. It is suitable
// for use with any of the Landsat SR datasets.
// Function to cloud mask from the pixel_qa band of Landsat 8 SR data.
function maskL8sr(image) {
// Bits 3 and 4 are cloud shadow and cloud, respectively.
var cloudShadowBitMask = 1 << 4;
var cloudsBitMask = 1 << 3;
// Get the pixel QA band.
var qa = image.select('QA_PIXEL');
// Both flags should be set to zero, indicating clear conditions.
var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
.and(qa.bitwiseAnd(cloudsBitMask).eq(0));
// Return the masked image, scaled to reflectance, without the QA bands.
return image.updateMask(mask)
.select("SR_B[0-9]*").multiply(0.0000275).add(-0.2)
.copyProperties(image, ["system:time_start"]);
}
// Map the function over one year of data.
var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
.filterBounds(ee.Geometry.Point(13.4,52.5))
.filterDate('2016-01-01', '2016-12-31')
.map(maskL8sr)
var composite = collection.median();
// Display the results.
Map.setCenter(13.4,52.5,10)
Map.addLayer(composite, {bands: ['SR_B4', 'SR_B3', 'SR_B2'], min: 0, max: 0.5});
@@ -0,0 +1,98 @@
// Detailed information: https://docs.google.com/document/d/1mNIRB90jwLuASO1JYas1kuOXCLbOoy1Z4NlV1qIXM10/edit
var imageCollection = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2");
var roi =
ee.Geometry.Polygon(
[[[120.34892531309845, 43.64119426062719],
[120.34892531309845, 43.618332108842225],
[120.39218398009064, 43.618332108842225],
[120.39218398009064, 43.64119426062719]]], null, false);
Map.centerObject(roi, 12)
var l8sr = imageCollection
.filterBounds(roi);
// This field contains UNIX time in milliseconds.
var timeField = 'system:time_start';
/////////////////////////////////////////////////////////////////////////
// CLOUD MASKING
// Function to cloud mask from the pixel_qa band of Landsat 8 SR data.
// (From the Code Editor Examples > Cloud Masking)
function maskL8sr(image) {
// Bit 0 - Fill
// Bit 1 - Dilated Cloud
// Bit 2 - Cirrus
// Bit 3 - Cloud
// Bit 4 - Cloud Shadow
var qa = image.select('QA_PIXEL')
var cloud = qa.bitwiseAnd(1 << 1)
.or(qa.bitwiseAnd(1 << 2))
.or(qa.bitwiseAnd(1 << 3))
.or(qa.bitwiseAnd(1 << 4))
// The flags should equal zero, indicating clear conditions.
var mask = cloud.eq(0);
//var ndwiMasked = ndwi.updateMask(ndwi.gte(0.4));
// Apply the scaling factors to the appropriate bands.
var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
// Replace the original bands with the scaled ones and apply the masks.
return image.addBands(opticalBands, null, true)
.updateMask(mask)
.copyProperties(image, ['system:time_start']);
}
/////////////////////////////////////////////////////////////////////////
// ADD BANDS
// Use this function to add variables for NDVI, time and a constant
// to Landsat 8 imagery.
var addVariables = function(image) {
// Compute time in fractional years since the epoch.
var date = ee.Date(image.get(timeField));
var years = date.difference(ee.Date('1970-01-01'), 'year');
// Return the image with the added bands.
return image
// Add an NDVI band.
.addBands(image.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI'))
// Add a time band.
.addBands(ee.Image(years).rename('t'))
.float()
// Add a constant band.
.addBands(ee.Image.constant(1));
};
// Remove clouds, add variables and filter to the area of interest.
var filteredLandsat = l8sr
.filterBounds(roi)
.map(maskL8sr)
.map(addVariables);
// Plot a time series of NDVI at a single location.
var l8Chart = ui.Chart.image.series(filteredLandsat.select('NDVI'), roi)
.setChartType('ScatterChart')
.setOptions({
title: 'Landsat 8 NDVI time series at ROI',
trendlines: {0: {
color: 'CC0000'
}},
lineWidth: 1,
pointSize: 3,
});
print(l8Chart);
// Display the NDVI of the time series (mean, first image, last image)
Map.addLayer(filteredLandsat.select("NDVI").mean(),{min:0, max:1}, "Mean NDVI Landsat 8")
Map.addLayer(filteredLandsat.select("NDVI").first(),{min:0, max:1}, "Oldest image within time series")
Map.addLayer(filteredLandsat.sort("system:time_start",false).select("NDVI").first(),{min:0, max:1}, "Newest image within time series")
@@ -0,0 +1,111 @@
// Detailed information: https://docs.google.com/document/d/1mNIRB90jwLuASO1JYas1kuOXCLbOoy1Z4NlV1qIXM10/edit
var imageCollection = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2");
var roi =
ee.Geometry.Polygon(
[[[120.34892531309845, 43.64119426062719],
[120.34892531309845, 43.618332108842225],
[120.39218398009064, 43.618332108842225],
[120.39218398009064, 43.64119426062719]]], null, false);
Map.centerObject(roi, 12)
var l8sr = imageCollection
.filterBounds(roi);
// This field contains UNIX time in milliseconds.
var timeField = 'system:time_start';
function maskL8sr(image) {
// Bit 0 - Fill
// Bit 1 - Dilated Cloud
// Bit 2 - Cirrus
// Bit 3 - Cloud
// Bit 4 - Cloud Shadow
var qa = image.select('QA_PIXEL')
var cloud = qa.bitwiseAnd(1 << 1)
.or(qa.bitwiseAnd(1 << 2))
.or(qa.bitwiseAnd(1 << 3))
.or(qa.bitwiseAnd(1 << 4))
// The flags should equal zero, indicating clear conditions.
var mask = cloud.eq(0);
//var ndwiMasked = ndwi.updateMask(ndwi.gte(0.4));
// Apply the scaling factors to the appropriate bands.
var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
// Replace the original bands with the scaled ones and apply the masks.
return image.addBands(opticalBands, null, true)
.updateMask(mask)
.copyProperties(image, ['system:time_start']);
}
// Use this function to add variables for NDVI, time and a constant
// to Landsat 8 imagery.
var addVariables = function(image) {
// Compute time in fractional years since the epoch.
var date = ee.Date(image.get(timeField));
var years = date.difference(ee.Date('1970-01-01'), 'year');
// Return the image with the added bands.
return image
// Add an NDVI band.
.addBands(image.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI'))
// Add a time band.
.addBands(ee.Image(years).rename('t'))
.float()
// Add a constant band.
.addBands(ee.Image.constant(1));
};
// Remove clouds, add variables and filter to the area of interest.
var filteredLandsat = l8sr
.filterBounds(roi)
.filter(ee.Filter.calendarRange(8,8, 'month'))
.map(maskL8sr)
.map(addVariables);
// Plot a time series of NDVI at a single location.
var l8Chart = ui.Chart.image.series(filteredLandsat.select('NDVI'), roi)
.setChartType('ScatterChart')
.setOptions({
title: 'Landsat 8 NDVI time series at ROI',
trendlines: {0: {
color: 'CC0000'
}},
lineWidth: 1,
pointSize: 3,
});
print(l8Chart);
// Display the overall mean NDVI of the time series
Map.addLayer(filteredLandsat.select('NDVI').first(), {min: 0, max: 1}, "Mean NDVI")
// List of the independent variable names
var independents = ee.List(['constant', 't']);
// Name of the dependent variable.
var dependent = ee.String('NDVI');
// Compute a linear trend. This will have two bands: 'residuals' and
// a 2x1 band called coefficients (columns are for dependent variables).
var trend = filteredLandsat.select(independents.add(dependent))
.reduce(ee.Reducer.linearRegression(independents.length(), 1));
Map.addLayer(trend, {}, 'trend array image');
// Flatten the coefficients into a 2-band image
var coefficients = trend.select('coefficients')
.arrayProject([0])
.arrayFlatten([independents]);
print(coefficients)
Map.addLayer(coefficients.select('t'), {min: -0.05, max: 0.05}, "gain b")
Map.addLayer(coefficients.select('constant'),{min: -0.1, max: 1}, "offset a")
@@ -0,0 +1,223 @@
/////// NDVI time series over Landsat 5,7 and 8
// to be defined
Map.centerObject(roi, 12)
var startyear = '1984-01-01'
var endyear = '2021-12-31'
var startmonth = 9
var endmonth = 9
//////////////////////////////////////////////////////////////////////////////////////////////////////
//// Functions that are applied to every image of a collection
// Due to the specific sensor characteristics of TM and OLI, specific function are needed for the sensors
// 1) Cloud masking
// 2) Rename Bands
// 3) Add NDVI and two bands to save the result of the linear regression (gain and constant)
// 1) Cloud masking
// Function to cloud mask from the pixel_qa band of Landsat 5 and 7 SR data.
// (From the Code Editor Examples > Cloud Masking)
function maskL457sr(image) {
// Bit 0 - Fill
// Bit 1 - Dilated Cloud
// Bit 2 - Cirrus
// Bit 3 - Cloud
// Bit 4 - Cloud Shadow
var qa = image.select('QA_PIXEL');
var cloud = qa.bitwiseAnd(1 << 0)
.or(qa.bitwiseAnd(1 << 1))
.or(qa.bitwiseAnd(1 << 2))
.or(qa.bitwiseAnd(1 << 3))
.or(qa.bitwiseAnd(1 << 4));
// The flags should equal zero, indicating clear conditions.
var mask = cloud.eq(0);
// Apply the scaling factors to the appropriate bands.
var opticalBands = image.select('SR_B1',"SR_B2", "SR_B3","SR_B4","SR_B5","SR_B7").multiply(0.0000275).add(-0.2);
// Replace the original bands with the scaled ones and apply the masks.
return image.addBands(opticalBands, null, true)
.updateMask(mask)
.copyProperties(image, ['system:time_start']);
}
// Function to cloud mask from the pixel_qa band of Landsat 8 SR data.
function maskL8sr(image) {
// Bit 0 - Fill
// Bit 1 - Dilated Cloud
// Bit 2 - Cirrus
// Bit 3 - Cloud
// Bit 4 - Cloud Shadow
var qa = image.select('QA_PIXEL');
var cloud = qa.bitwiseAnd(1 << 0)
.or(qa.bitwiseAnd(1 << 1))
.or(qa.bitwiseAnd(1 << 2))
.or(qa.bitwiseAnd(1 << 3))
.or(qa.bitwiseAnd(1 << 4));
// The flags should equal zero, indicating clear conditions.
var mask = cloud.eq(0);
// Apply the scaling factors to the appropriate bands.
var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
// Replace the original bands with the scaled ones and apply the masks.
return image.addBands(opticalBands, null, true)
.updateMask(mask)
.copyProperties(image, ['system:time_start']);
}
// 2) Rename Bands
// Function to rename bands of Landsat 5 and 7 to match Landsat 8
function renameBandsTM(image) {
var bands = ['SR_B1', 'SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B7'];
var new_bands = ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'];
return image.select(bands).rename(new_bands).copyProperties(image, ['system:time_start']);
}
// Function to rename bands of Landsat 8 to match Landsat 5 and 8
function renameBandsOLI(image){
var bands = ['SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B6', 'SR_B7'];
var new_bands = ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'];
return image.select(bands).rename(new_bands).copyProperties(image, ['system:time_start']);
}
// 3) Add NDVI and two bands to save the result of the linear regression (gain and constant)
// This field contains UNIX time in milliseconds.
var timeField = 'system:time_start';
function addVariables(image) {
// Compute time in fractional years since the epoch.
var date = ee.Date(image.get(timeField));
var years = date.difference(ee.Date('1970-01-01'), 'year');
// Return the image with the added bands.
return image
// Add an NDVI band.
.addBands(image.normalizedDifference(['NIR', 'R']).rename('NDVI'))
// Add a time band.
.addBands(ee.Image(years).rename('gain'))
.float()
// Add a constant band.
.addBands(ee.Image.constant(1));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Create collections of Landsat 5, 7 and 8
// a) using filters (calendar range and ROI)
// b) using the three functions (cloud masking, rename bands, add NDVI)
// c) Merge the collections --> combine the three collections to form a time series over the entire time span
// Landat 5 surface reflection data
var L5coll = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
.filterBounds(roi)
.filter(ee.Filter.calendarRange(startmonth,endmonth, 'month'))
.filterDate(startyear, endyear)
.map(maskL457sr)
.map(renameBandsTM)
.map(addVariables);
// Landat 7 surface reflection data
var L7coll = ee.ImageCollection('LANDSAT/LE07/C02/T1_L2')
.filter(ee.Filter.calendarRange(startmonth,endmonth, 'month'))
.filterDate(startyear, endyear)
.filterBounds(roi)
.map(maskL457sr)
.map(renameBandsTM)
.map(addVariables);
// Landat 8 surface reflection data, rename the band names. See USGS pages for more info
var L8coll = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
.filter(ee.Filter.calendarRange(startmonth,endmonth, 'month'))
.filterDate(startyear, endyear)
.filterBounds(roi)
.map(maskL8sr)
.map(renameBandsOLI)
.map(addVariables)
// merge L5, L7 & L8
var collection_merge = ee.ImageCollection(L5coll.merge(L7coll.merge(L8coll)));
print (collection_merge)
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Plot time series for an ROI
// Plot a time series of NDVI at a single location.
var l8Chart = ui.Chart.image.series(collection_merge.select('NDVI'), roi)
.setChartType('ScatterChart')
.setOptions({
title: 'Landsat NDVI time series at ROI',
trendlines: {0: {
color: 'CC0000'
}},
lineWidth: 1,
pointSize: 3,
});
print(l8Chart);
// Display the NDVI of the time series (mean, first image, last image)
Map.addLayer(collection_merge.select("NDVI").mean(),{min:0, max:1}, "Mean NDVI Landsat 8")
Map.addLayer(collection_merge.select("NDVI").first(),{min:0, max:1}, "Oldest image within time series")
Map.addLayer(collection_merge.sort("system:time_start",false).select("NDVI").first(),{min:0, max:1}, "Newest image within time series")
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Trend analysis
// Calculate the constant and gain of the linear regression of the NDVI on a per-pixel basis
Map.centerObject(roi, 11)
// List of the independent variable names
var independents = ee.List(['constant', 'gain']);
// Name of the dependent variable.
var dependent = ee.String('NDVI');
// Compute a linear trend. This will have two bands: 'residuals' and
// a 2x1 band called 'coefficients' (columns are for dependent variables).
var trend = collection_merge.select(independents.add(dependent))
.reduce(ee.Reducer.linearRegression(independents.length(), 1));
// Map.addLayer(trend, {}, 'trend array image');
// Flatten the coefficients into a 2-band image
var coefficients = trend.select('coefficients')
.arrayProject([0])
.arrayFlatten([independents]);
// Plot the two bands as gray scale
Map.addLayer(coefficients.select('gain'), {min:-0.01, max: 0.01}, "gain")
Map.addLayer(coefficients.select('constant'), {},"constant")
// Plot the two bands in a RGB-False colour Composite (positive trends = greenish, negative trends = reddish)
Map.addLayer(coefficients,
{min: 0, max: [-0.02, 0.02, 0.7], bands: ['gain', 'gain', 'constant']},
'Trend analysis')
@@ -0,0 +1,108 @@
//The Hansen et al. (2013) Global Forest Change dataset in Earth Engine represents
//forest change, at 30 meters resolution, globally, between 2000 and 2014. Let's start
//by adding the Hansen et al. data to the map. Import the global forest change
//data (learn more about searching and importing datasets) by searching for "Hansen forest"
//and naming the import gfc2014:
//Recall that when a multi-band image is added to a map, the first three bands of the
//image are chosen as red, green, and blue, respectively, and stretched according to the
//data type of each band. The reason the image looks red is that the first three bands
//are treecover2000, loss, and gain. The treecover2000 band is expressed as a percent
//and has values much higher than loss (green) and gain (blue) which are binary ({0, 1}).
//The image therefore displays as overwhelmingly red.
var gfc2014 = ee.Image("UMD/hansen/global_forest_change_2020_v1_8");
Map.addLayer(gfc2014);
//To display forest cover in the year 2000 as a grayscale image,
//you can use the treecover2000 band, specified in the second argument
//to Map.addLayer():
Map.addLayer(gfc2014, {bands: ['treecover2000']}, 'treecover2000');
///
//Display the band that differentiates land surface and permanent water bodies.
// Set min and max in the display options!
Map.addLayer(gfc2014, {bands: ['datamask'], min:0, max:2}, 'datamask');
//Here's an image that uses 3 bands, Landsat bands 5, 4, and 3 for 2015.
// This band combination shows healthy vegetation as green and soil as mauve:
Map.addLayer(
gfc2014, {bands: ['last_b50', 'last_b40', 'last_b30']}, 'false color');
//One nice visualization of the Global Forest Change dataset shows forest extent
//in 2000 as green, forest loss as red, and forest gain as blue.
//Specifically, make loss the first band (red), treecover2000 the second band (green),
//and gain the third band (blue):
Map.addLayer(gfc2014, {bands: ['loss', 'treecover2000', 'gain']}, 'green');
//We'd like forest loss to show up as bright red and forest gain to show up as bright
//blue. To fix this, we can use the visualization parameter max to set the range to
//which the image data are stretched. Note that the max visualization parameter
//takes a list of values, corresponding to maxima for each band:
Map.addLayer(gfc2014, {
bands: ['loss', 'treecover2000', 'gain'],
max: [1, 255, 1]
}, 'forest cover, loss, gain');
/// Improve displaying tree cover (forest extent)
//Use a green palette to display the forest extent image:
Map.addLayer(gfc2014, {
bands: ['treecover2000'],
palette: ['000000', '00FF00']
}, 'forest cover palette');
//The image shown in Figure 8 is a bit dark. The problem is that the treecover2000
//band has a byte data type ([0, 255]), when in fact the values are precentages ([0, 100]).
//To brighten the image, you can set the min and/or max parameters accordingly.
// The palette is then stretched between those extrema.
Map.addLayer(gfc2014, {
bands: ['treecover2000'],
palette: ['000000', '00FF00'],
max: 100
}, 'forest cover percent');
//All of the images shown so far have had big black areas were there the data is zero.
//For example, there are no trees in the ocean. To make these areas transparent,
//you can mask their values. Every pixel in Earth Engine has both a value and a mask.
//The image is rendered with transparency set by the mask, with zero being completely
//transparent and one being completely opaque.
//You can mask an image with itself. For example, if you mask the treecover2000 band
//with itself, all the areas in which forest cover is zero will be transparent:
Map.addLayer(gfc2014.mask(gfc2014), {
bands: ['treecover2000'],
palette: ['000000', '00FF00'],
max: 100
}, 'forest cover masked');
//You can mask an image by a mask. For example, if you mask the treecover2000 band
//where all the values are unter 10%, i.e. you want to keep the areas that have at
//least 10% tree cover, all the areas in which forest cover is zero will be transparent:
var mask10 = gfc2014.select("treecover2000").gte(10)
Map.addLayer(gfc2014.mask(mask10), {
bands: ['treecover2000'],
palette: ['000000', '00FF00'],
min:0, max: 100
}, 'forest cover masked - >10%');
// The same as before but with a threshold of 30%.
var mask30 = gfc2014.select("treecover2000").gte(30)
Map.addLayer(gfc2014.mask(mask30), {
bands: ['treecover2000'],
palette: ['000000', '00FF00'],
min:0, max: 100
}, 'forest cover masked - >30%');
@@ -0,0 +1,22 @@
//It's almost possible to make a visualization of the Hansen data like the one at the
//beginning of the tutorial. In this example, we're putting everything together
//with one small difference. Instead of specifying the bands parameter in the
//Map.addLayer call, we're creating new images using select():
var gfc2014 = ee.Image("UMD/hansen/global_forest_change_2020_v1_8");
var treeCover = gfc2014.select(['treecover2000']);
var lossImage = gfc2014.select(['loss']);
var gainImage = gfc2014.select(['gain']);
// Add the tree cover layer in green.
Map.addLayer(treeCover.updateMask(treeCover),
{palette: ['000000', '00FF00'], max: 100}, 'Forest Cover');
// Add the loss layer in red.
Map.addLayer(lossImage.updateMask(lossImage),
{palette: ['FF0000']}, 'Loss');
// Add the gain layer in blue.
Map.addLayer(gainImage.updateMask(gainImage),
{palette: ['0000FF']}, 'Gain');
@@ -0,0 +1,51 @@
//Let's start with the calculation needed to create a band that shows pixels
//where the Hansen et al. data show both loss and gain.
//The Hansen et al. dataset has a band whose pixels are 1 where loss occurred
//and 0 otherwise (loss) and a band that is 1 where gain has occurred and a 0 otherwise (gain).
//To create a band where pixels in both the loss and the gain bands have a 1,
//you can use the and() logical method on images. The and() method is called
//like image1.and(image2) and returns an image in which pixels are 1 where both
//image1 and image2 are 1, and 0 elsewhere:
// Load the data and select the bands of interest.
var gfc2014 = ee.Image("UMD/hansen/global_forest_change_2020_v1_8");
var lossImage = gfc2014.select(['loss']);
var gainImage = gfc2014.select(['gain']);
// Use the and() method to create the lossAndGain image.
var gainAndLoss = gainImage.and(lossImage);
// Show the loss and gain image.
Map.addLayer(gainAndLoss.updateMask(gainAndLoss),
{palette: 'FF00FF'}, 'Gain and Loss');
//Combining this example with the result from the previous section,
//it's now possible to recreate the figure from the beginning of the tutorial:
// Displaying forest, loss, gain, and pixels where both loss and gain occur.
var gfc2014 = ee.Image('UMD/hansen/global_forest_change_2015');
var lossImage = gfc2014.select(['loss']);
var gainImage = gfc2014.select(['gain']);
var treeCover = gfc2014.select(['treecover2000']);
// Use the and() method to create the lossAndGain image.
var gainAndLoss = gainImage.and(lossImage);
// Add the tree cover layer in green.
Map.addLayer(treeCover.updateMask(treeCover),
{palette: ['000000', '00FF00'], max: 100}, 'Forest Cover');
// Add the loss layer in red.
Map.addLayer(lossImage.updateMask(lossImage),
{palette: ['FF0000']}, 'Loss');
// Add the gain layer in blue.
Map.addLayer(gainImage.updateMask(gainImage),
{palette: ['0000FF']}, 'Gain');
// Show the loss and gain image.
Map.addLayer(gainAndLoss.updateMask(gainAndLoss),
{palette: 'FF00FF'}, 'Gain and Loss');
@@ -0,0 +1,88 @@
//Now that you're more familiar with the bands in the Hansen et al. dataset,
//we can use concepts learned so far to compute statistics about forest gain
//and loss in a region of interest. For this we'll need to use vector data
//(points, lines, and polygons). A vector dataset is represented as a
//FeatureCollection in Earth Engine. (Learn more about feature collections
//and how to import vector data.)
//In this section, we'll compare the total amount of forest loss that happened
//within the Republic of the Congo in the year 2012 to the amount of forest loss that
//happened within the country's protected areas at the same time.
//As you learned in the Earth Engine API tutorial, the key method for calculating
//statistics in an image region is reduceRegion(). (Learn more about reducing image
//regions.) For example, suppose we want to calculate the number of pixels estimated
//to represent forest loss during the study period. For that purpose, consider the
//following code.
//The example uses the ee.Reducer.sum() reducer to sum the values of the
//pixels in lossImage within the Republic of the Congo feature. Because lossImage consists
//of pixels that have a value of 1 or 0 (for loss or not loss, respectively),
//the sum of these values is equivalent to the number of pixels of loss in the
//region.
// Load country features from Large Scale International Boundary (LSIB) dataset.
var countries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Subset the Republic of the Canada feature from countries.
var germany = countries.filter(ee.Filter.eq('country_na', 'Germany'));
// Get the forest loss image.
var gfc2014 = ee.Image("UMD/hansen/global_forest_change_2020_v1_8");
var lossImage = gfc2014.select(['loss']);
// Sum the values of forest loss pixels in Canada.
var stats = lossImage.reduceRegion({
reducer: ee.Reducer.sum(),
geometry: germany,
scale: 30,
maxPixels: 2e9
});
print(stats);
//By expanding the object printed to the console, observe that the
//result is 4897933 (in the original example!) pixels of forest lost. You can
//clean up the printout in the console a bit by labeling the output
//and getting the result of interest from the dictionary returned
//by reduceRegion():
print('pixels representing loss: ', stats.get('loss'));
//You're almost ready to answer the question of how much area was lost in
//the Republic of the Congo.
//The remaining part is to convert pixels into actual area. This conversion
//is important because we don't necessarily know the size of the pixels input
//to reduceRegion(). To help compute areas, Earth Engine has the ee.Image.pixelArea()
//method which generates an image in which the value of each pixel is the pixel's
//area in square meters. Multiplying the loss image with this area image and then
//summing over the result gives us a measure of area:
// Load country features from Large Scale International Boundary (LSIB) dataset.
var countries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Subset Republic of the Congo feature from countries.
var germany = countries.filter(ee.Filter.eq('country_na', 'Germany'));
// Get the forest loss image.
//var gfc2014 = ee.Image('UMD/hansen/global_forest_change_2015'); //we did already open that one
var lossImage = gfc2014.select(['loss']);
var areaImage = lossImage.multiply(ee.Image.pixelArea()).divide(1000000);
// Sum the values of forest loss pixels in Canada.
var stats = areaImage.reduceRegion({
reducer: ee.Reducer.sum(),
geometry: germany,
scale: 30,
maxPixels: 2e9
});
print('pixels representing loss: ', stats.get('loss'), 'square km');
//You are now ready to answer the question at the start of this section - how
//much forest area was lost in the Republic of the Congo in 2012, and how much of that
//was in protected areas? Have a look in the next script.
@@ -0,0 +1,67 @@
//You are now ready to answer the question at the start of this
//section - how much forest area was lost in the Republic of the Congo in
//2012, and how much of that was in protected areas?
// Load country features from Large Scale International Boundary (LSIB) dataset.
var countries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Subset the Congo Republic feature from countries.
var congo = ee.Feature(
countries
.filter(ee.Filter.eq('country_na', 'Rep of the Congo'))
.first()
);
// Subset protected areas to the bounds of the feature of the Republic of the Congo
// and other criteria. Clip to the intersection with the Republic of the Congo.
var protectedAreas = ee.FeatureCollection('WCMC/WDPA/current/polygons')
.filter(ee.Filter.and(
ee.Filter.bounds(congo.geometry()),
ee.Filter.neq('IUCN_CAT', 'VI'),
ee.Filter.neq('STATUS', 'proposed'),
ee.Filter.lt('STATUS_YR', 2010)
))
.map(function(feat){
return congo.intersection(feat);
});
// Get the loss image.
var gfc2014 = ee.Image('UMD/hansen/global_forest_change_2015');
var lossIn2012 = gfc2014.select(['lossyear']).eq(12);
var areaImage = lossIn2012.multiply(ee.Image.pixelArea());
// Calculate the area of loss pixels in the Republic of the Congo.
var stats = areaImage.reduceRegion({
reducer: ee.Reducer.sum(),
geometry: congo.geometry(),
scale: 30,
maxPixels: 1e9
});
print(
'Area lost in the Republic of the Congo:',
stats.get('lossyear'),
'square meters'
);
// Calculate the area of loss pixels in the protected areas.
var stats2 = areaImage.reduceRegion({
reducer: ee.Reducer.sum(),
geometry: protectedAreas.geometry(),
scale: 30,
maxPixels: 2e9
});
print(
'Area lost in protected areas:',
stats2.get('lossyear'),
'square meters'
);
//The only changes between this script and the one just prior are the
//addition of the protected area information and changing the script
//from looking at overall loss to looking at loss in 2012. This required
//two changes. First, there's a new lossIn2012 image which has a 1 where loss
//was recorded in 2012, 0 otherwise. Second, because the name of the band is different
//(lossyear instead of loss) the property name had to change in the print statement.
@@ -0,0 +1,70 @@
//In the previous section you learned how to calculate total forest area lost in
//the given region of interest using the reduceRegion method. Instead of
//calculating the total loss, it would be helpful to compute the loss for
//each year. The way to achieve this in Earth Engine is using a Grouped Reducer.
//To group output of reduceRegion(), you can specify a grouping band that
//defines groups by integer pixel values. In the following example, we
//slightly modify the previous code and add the lossYear band to the original
//image. Each pixel in the lossYear band contain values from 0 to 14 - indicating
//the year in which the loss occurred. We also change the reducer to a grouped
//reducer, specifying the band index of the grouping band (1) so the pixel
//areas will be summed and grouped according to the value in the lossYear band.
// Load country boundaries from LSIB.
var countries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Get a feature collection with just the feature of the Republic of the Congo.
var congo = countries.filter(ee.Filter.eq('country_na', 'Germany'));
// Get the loss image.
// This dataset is updated yearly, so we get the latest version.
var gfc2017 = ee.Image("UMD/hansen/global_forest_change_2020_v1_8");
var lossImage = gfc2017.select(['loss']);
var lossAreaImage = lossImage.multiply(ee.Image.pixelArea());
var lossYear = gfc2017.select(['lossyear']);
var lossByYear = lossAreaImage.addBands(lossYear).reduceRegion({
reducer: ee.Reducer.sum().group({
groupField: 1
}),
geometry: congo,
scale: 30,
maxPixels: 1e10
});
print(lossByYear);
//Once you run the above code, you will see the yearly forest
//loss area printed out in a nested list called groups. We can format
//the output a little to make the result a dictionary, with year as the
//key and loss area as the value. Notice that we are using the format()
//method to convert the year values from 0-14 to 2000-2014.
var statsFormatted = ee.List(lossByYear.get('groups'))
.map(function(el) {
var d = ee.Dictionary(el);
return [ee.Number(d.get('group')).format("20%02d"), d.get('sum')];
});
var statsDictionary = ee.Dictionary(statsFormatted.flatten());
print(statsDictionary);
//Now that we have yearly loss numbers, we are ready to prepare a chart.
//We will use the ui.Chart.array.values() method. This method takes an array
//(or list) of input values and an array (or list) of labels for the X-axis.
var chart = ui.Chart.array.values({
array: statsDictionary.values(),
axis: 0,
xLabels: statsDictionary.keys()
}).setChartType('ColumnChart')
.setOptions({
title: 'Yearly Forest Loss in Germany',
hAxis: {title: 'Year', format: '####'},
vAxis: {title: 'Area (square meters)'},
legend: { position: "none" },
lineWidth: 1,
pointSize: 3
});
print(chart);
// This is how you can add a vector layer to the map view.
Map.addLayer(congo, {color: 'FF0000'}, 'Germany');