Skip to content

Instantly share code, notes, and snippets.

@okjodom
Last active November 16, 2021 19:27
Show Gist options
  • Save okjodom/38bdc7dfded2a299dda7cbf1a00bd1e3 to your computer and use it in GitHub Desktop.
Save okjodom/38bdc7dfded2a299dda7cbf1a00bd1e3 to your computer and use it in GitHub Desktop.
Google earth engine water quality analysis, Mbita point study area
/*
Water Quality Analysis in the Winam Gulf of Lake Victoria
Water Quality Parameter: Chlorophyll-a
#####################
Specific Study Area: Mbita Point
Datasets: Landat 5 and Landsat 7 Satellite Imagery
Epochs: December 31st of the years [1984, 1988, 1992, 1996, 2000, 2004, 2008, 2016, 2016]
Test Epochs: 1984 and 2016 (Sentinel Epochs)
Algorithms: Color Index (CI) by Hu et al. 2012
Assets:
gulfclip: Extent clipper for the Winam Gulf
pointclip: Extent clipper for the Mbita Point
gulfpoly: Shoreline of the Winam Gulf
pointpoly: Shoreline of the Mbita Point
profile(X): waypoints for deriving profiles
Published App
https://jodom.users.earthengine.app/view/mbita-causeway
DISCLAIMER
This is not a published, citable, scientific study. Each attriutable input to this script (example; CI algorithm, assets, Earth Engine) may be subject to its own licensing copyrights.
The script is free for copy and use, and constitutes zero liability to the author
*/
var pointclip = ee.FeatureCollection("users/jodom/causeway/pointclip"),
pointpoly = ee.FeatureCollection("users/jodom/causeway/point_polygon"),
gulfline = ee.FeatureCollection("users/jodom/causeway/gulf_shoreline"),
gulfpoly = ee.FeatureCollection("users/jodom/causeway/gulf_polygon"),
profile0 = ee.FeatureCollection("users/jodom/causeway/profile0"),
profile1 = ee.FeatureCollection("users/jodom/causeway/profile1"),
profile2 = ee.FeatureCollection("users/jodom/causeway/profile2"),
profile3 = ee.FeatureCollection("users/jodom/causeway/profile3"),
profile4 = ee.FeatureCollection("users/jodom/causeway/profile4"),
profile5 = ee.FeatureCollection("users/jodom/causeway/profile5"),
profile6 = ee.FeatureCollection("users/jodom/causeway/profile6"),
profile7 = ee.FeatureCollection("users/jodom/causeway/profile7"),
profiles = ee.FeatureCollection("users/jodom/causeway/1278profiles");
// defaults
var extent = gulfpoly; // extent of analysis
var profilesOn = false; // status of analysis profiles
// constants
var waypoints = [profile0, profile1, profile2, profile3, profile4, profile5, profile6, profile7];
var sampledpoints = [profile0, profile1, profile6, profile7];
// ####################### Sourcing Data #
function getData(){
/* Import, filter and transforms Satellite Images */
// Filtered Surface Reflectance (SR) Collections
var sr5coll = ee.ImageCollection("LANDSAT/LT05/C01/T1_SR").filterBounds(pointclip).filterDate('1982-01-01', '2000-12-31').filter(ee.Filter.lte('CLOUD_COVER', 15));
var sr7coll = ee.ImageCollection("LANDSAT/LE07/C01/T1_SR").filterBounds(pointclip).filterDate('2000-01-01', '2018-12-31').filter(ee.Filter.lte('CLOUD_COVER', 15));
// Compute representative averages for each epoch
var epochAvgs = [
ee.Image(sr5coll.filterDate('1984-01-01', '1984-12-31').mean()).set("Year", 1984),
ee.Image(sr5coll.filterDate('1994-01-01', '1994-12-31').mean()).set("Year", 1994),
ee.Image(sr7coll.filterDate('2004-01-01', '2004-12-31').mean()).set("Year", 2004),
ee.Image(sr7coll.filterDate('2014-01-01', '2014-12-31').mean()).set("Year", 2014),
ee.Image(sr7coll.filterDate('2017-01-01', '2017-12-31').mean()).set("Year", 2017)
];
return epochAvgs;
}
// filtered dataset
var dataset = getData();
// ####################### CHL-a Analysis Algorithms ##
// CI Algorithm, (Hu et al. 2012)
function CI(image){
/*
Calculates the Color Index (CI) by difference in reflectance of bands from input image
Formulation:
result = Green - [ Blue + (lambdaGreen - lambdaBlue) / (lambdaRed - lambdaBlue) * (Red - Blue) ]
Where :
Red, Green, Blue are Reflectances in the respective bands of sat image
lambdaRed, lambdaGreen, lambdaBlue
are instrument-specific wavelengths closest to 670, 555, 443 respectively of bands
*/
var result = image.expression(
'Green - ( Blue + (lambdaGreen - lambdaBlue) / (lambdaRed - lambdaBlue) * (Red - Blue) )',
{
'Red' : image.select('B3'), // *Designations for SR datasets>>
'lambdaRed' : 670,
'Green' : image.select('B2'),
'lambdaGreen' : 555,
'Blue' : image.select('B1'),
'lambdaBlue' : 443
});
return result;
}
// ####################### Visualizations ####
function computeChlSurfaces(algorithm){
/**/
var result = [];
dataset.forEach(function(image){
result.push(CI(image).set('Year', image.get('Year'))); // compute chl-a surfaces
});
return ee.ImageCollection(result);
// return result;
}
// color pallete
var chlaPallete = ['51f1e3', 'ffd761', 'ff6437', 'ff0414'];
function buildChart(collection){
/*
Generates a time series chart with specifications provided
[ImageCollection]@collection: computed chl-a surfaces
*/
// A time series chart from computed surfaces
var chart = ui.Chart.image.series({
imageCollection: collection,
region: extent,
reducer: ee.Reducer.mean(),
scale: 30,
xProperty: 'Year'
});
// Custom chart options
chart.setOptions({
title: 'CI Algorithm',
legend: {position: 'none'},
vAxis: {title: 'chl-a estimates (avg)'},
hAxis: {title: 'Year', format: '', minValue: 1984},
// backgroundColor: { fill: "#F4F4F4"},
pointSize: 5,
pointShape: 'circle'
});
// Chart styling
chart.style().set({
width: '380px',
height: '300px'
});
// Update the map and label when the chart is clicked.
chart.onClick(function(xValue, yValue, seriesName) {
if (!xValue) return; // Selection was cleared.
// Show the image for the clicked year.
var image = ee.Image(collection.filter(ee.Filter.equals('Year', xValue)).first()).clip(extent);
var layer = ui.Map.Layer(image, {
min: 0,
max: 250,
palette: chlaPallete,
bands: 'B2'
});
profilesOn ? Map.layers().reset([layer, profiles]) : Map.layers().reset([layer]);
// Show a label with the date on the map.
label.setValue(xValue + ' CHL-A LEVELS FROM CI ALGORITHM');
});
return chart;
}
function buildProfile(profile, image){
var chart = ui.Chart.image.byRegion({
image: image,
regions: profile,
scale: 30,
xProperty: 'Label'
});
// Custom chart options
chart.setOptions({
curveType: 'function',
title: 'Chlorophyll-a Levels',
legend: {position: 'none'},
vAxis: {title: 'average chl-a level', minValue: 0},
hAxis: {title: 'Profile', format: 'short', minValue: 0},
backgroundColor: { fill: "#F4F4F4"},
pointSize: 4,
lineWidth: 1
});
// Chart styling
chart.style().set({
width: '500px',
height: '300px'
});
return chart;
}
// Create the UI
// Scale Picker :: different scales of analysis
var analysisExtent = {
'Choose An Analysis Scale': 'default',
'Mbita Point': 'point',
'Winam Gulf': 'gulf'
};
var selectScale = ui.Select({
items: Object.keys(analysisExtent),
onChange: function(value){
// render analysis extent and charts at scale selected
if (analysisExtent[value] == 'point'){
extent = pointpoly;
Map.setCenter(34.207236, -0.425424, 12);
}else if (analysisExtent[value] == 'gulf'){
extent = gulfpoly;
Map.setCenter( 34.407875, -0.423000, 10);
}else{
return;
}
rightPanel.widgets().set(1, buildChart(ci_collection));
},
style: { width: '365px', color: 'red'}
});
selectScale.setPlaceholder('Choose Scale of Analysis');
var rightPanel = ui.Panel({
layout: ui.Panel.Layout.flow('vertical'),
style: {
position: 'top-right',
width: '400px'
}
});
var buttonPanel = ui.Panel({
layout: ui.Panel.Layout.flow('horizontal'),
style: {
width: '365px'
}
});
buttonPanel.widgets().set(0,
ui.Button({
label: 'Show Profiles',
style: { width: '45%', color: 'red'},
onClick: function() {
if (! profilesOn){
Map.addLayer(profiles, {}, 'Profiles');
}
profilesOn = true;
}
})
);
buttonPanel.widgets().set(1,
ui.Button({
label: 'Clear',
style: { width: '45%', color: 'red'},
onClick: function() {
Map.layers().reset();
profilesOn = false;
}
})
);
// CI ##
var ci_collection = computeChlSurfaces('CI');
// A time series chart from computed surfaces
var ciChart = buildChart(ci_collection);
// Add charts to panel
rightPanel.widgets().set(0, selectScale);
rightPanel.widgets().set(1, ciChart);
rightPanel.widgets().set(2, buttonPanel);
// Display panel
Map.add(rightPanel);
// Create a label on the map.
var label = ui.Label('Click a point on the chart to show the Chl-a surface');
Map.add(label);
Map.setCenter( 34.407875, -0.423000, 10);
// profiles
// ci_collection.map(function(image){
// print(image)
// // print(image.get('Year'));
// waypoints.forEach(function(pointgroup){
// pointgroup = pointgroup.sort('Id');
// print(buildProfile(pointgroup, image));
// });
// });
// set position of panel
var legend = ui.Panel({
style: {
position: 'bottom-center',
padding: '8px 15px',
margin: '0 0 100px 100px'
}
});
// Create legend title
var legendTitle = ui.Label({
value: 'Chl-a Levels (mg/L)',
style: {
fontWeight: 'bold',
fontSize: '18px',
margin: '0 0 4px 0',
padding: '0'
}
});
// Add legend to maps
// Add the title to the panel
legend.add(legendTitle);
// Creates and styles 1 row of the legend.
var makeRow = function(color, name) {
// Create the label that is actually the colored box.
var colorBox = ui.Label({
style: {
backgroundColor: '#' + color,
// Use padding to give the box height and width.
padding: '8px',
margin: '0 0 4px 0'
}
});
// Create the label filled with the description text.
var description = ui.Label({
value: name,
style: {margin: '0 0 4px 6px'}
});
// return the panel
return ui.Panel({
widgets: [colorBox, description],
layout: ui.Panel.Layout.Flow('horizontal')
});
};
// Palette with the colors
var pallete = ['51f1e3', 'ffd761', 'ff6437', 'ff0414'];
// name of the legend
var names = ['Below 50', '50 - 100', '100-200', 'Above 200'];
// Add color and and names
for (var i = pallete.length; i > 0; i--) {
legend.add(makeRow( pallete[i-1], names[i-1]));
}
// add legend to map (alternatively you can also print the legend to the console)
Map.add(legend);
@xiongcunzhang
Copy link

It's helpful, if you can share the asserts (including the pointclip etc) to us that we can run the code . Thanks very much!

@mcarslanoglu
Copy link

Please, can you share your assets..

@okjodom
Copy link
Author

okjodom commented Jan 2, 2020

It's helpful, if you can share the asserts (including the pointclip etc) to us that we can run the code

The assets [ gulfclip: Extent clipper for the Winam Gulf
pointclip: Extent clipper for the Mbita Point
gulfpoly: Shoreline of the Winam Gulf
pointpoly: Shoreline of the Mbita Point
profile(X): waypoints for deriving profiles ]
were not meant to be public, otherwise they would have been posted in a repo and not gist.

Now you could copy this gist to your earth engine editor, point to your own assets you upload and manage in E.E ( for whatever water body you want to perform an analysis) and run.

Happy to help.

@mcarslanoglu
Copy link

Dear Jodom...
Can you explain your assets as points, polygon, polyline etc. please?
Please, give an explanation...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment