Green Chlorophyll index - Sentinel 2 GEE Palette - javascript

I am trying to load a map and be able to see the GRI(Green Chlorophyll index) on a certain area. I tried doing it with NDVI, however when I do it for GRI, I get an entirely black area.
I'm not really sure what Im doing wrong especially that I'm new to this.
Any tip would be really appreciated!
var AOI = Boundary_Graf;
var points = Points_Graf;
//Display the exact location of the points
var shp = ee.FeatureCollection(points);
Map.addLayer(shp, {}, 'Graf.Points');
//Display the exact location of the polygon
var shp = ee.FeatureCollection(AOI);
Map.addLayer(shp, {}, 'Polygon_Graf');
//Open S2
var S2 = ee.ImageCollection("COPERNICUS/S2")
//Filter for a specific date (now just chose it randomly)
. filterDate("2022-01-01", "2022-06-22")
//Filter for the polygon area
. filterBounds(AOI)
//Filter data based on metadata property -> cloudy pixel percentage <5%
.filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 5)
// Subsetting the image based on AOI
.map(function(image){return image.clip(AOI)})
// Printing the S2 layer to the console
print ("S2 collection:", S2);
var trueColour = {bands:["B8", "B4", "B3"], max: 3048, gamma:1};
var visParams = {min: 0, max: 0.8, palette: 'FFFFFF, CE7E45, DF923D, F1B555, FCD163, 99B718, 74A901, 66A000, 529400,' +
'3E8601, 207401, 056201, 004C00, 023B01, 012E01, 011D01, 011301'};
// Calculate NDVI
var NDVI_collection = S2.map(function(image){
var NDVI = image.normalizedDifference(['B8','B4']).rename('NDVI');
return NDVI
.copyProperties(image, ['system:time_start']);
});
// Calculate GCI
var CGI_collection = S2.map (function(image){
var CGI = image.expression ('(((NIR) / (GREEN)) - 1)', {
'NIR': image.select ('B8'),
'GREEN': image.select ('B3')
}).rename("GCI");
return CGI;
});
Map.centerObject(AOI);
Map.addLayer(S2, trueColour, 'true-colour image');
Map.addLayer(NDVI_collection, visParams,'Sentinel-2 NDVI');
Map.addLayer(CGI_collection, visParams, 'Sentinel-2 CGI');

Related

How to clip a cloud mask function in google earth engine?

I'm looking to clip the cloud mask function (top of my code) to the AOI. For some reason, my other layers (dNDVI and RGB) work for the AOI, but my cloud mask doesn't apply to it. I've tried putting in the ee.Image.clip function but I couldn't get it to work.
I would love to have some insight as to why it won't apply for this specific layer. Very new to coding!
// --------- Cloud mask: -----------------
var mask_all = function(image) {
var hollstein_mask = cld.hollstein_S2(['shadow', 'cloud', 'cirrus'])(image);
return hollstein_mask;
};
var color = "#98ff00";
var AOI = ee.Geometry.Polygon(
[[[140.88237590701584,-37.896469860299604],
[140.96546001346115,-37.896469860299604],
[140.96546001346115,-37.83819826191272],
[140.88237590701584,-37.83819826191272],
[140.88237590701584,-37.896469860299604]]], null, false);
/* [[[140.5710269570096,-37.669974265519755],
[140.64037815329866,-37.669974265519755],
[140.64037815329866,-37.60037237657578],
[140.5710269570096,-37.60037237657578],
[140.5710269570096,-37.669974265519755] */
//load images for composite
var Sen2mosaic = ee.ImageCollection("COPERNICUS/S2_SR");
ee.Geometry.Polygon(140.88237590701584,-37.896469860299604);
var getQABits = function(image, start, end, newName) {
// Compute the bits we need to extract.
var pattern = 0;
for (var i = start; i <= end; i++) {
pattern += Math.pow(2, i);
}
// Return a single band image of the extracted QA bits, giving the band
// a new name.
return image.select([0], [newName])
.bitwiseAnd(pattern)
.rightShift(start);
};
// A function to mask out cloudy pixels.
var cloud_shadows = function(image) {
// Select the QA band.
var QA = image.select(['B8', 'B11', 'B4']);
// Get the internal_cloud_algorithm_flag bit.
return getQABits(QA, 3,3, 'cloud_shadows').eq(0);
// Return an image masking out cloudy areas.
};
// A function to mask out cloudy pixels.
var clouds = function(image) {
// Select the QA band.
var QA = image.select(['B8', 'B11', 'B4']);
// Get the internal_cloud_algorithm_flag bit.
return getQABits(QA, 5,5, 'Cloud').eq(0);
// Return an image masking out cloudy areas.
};
var maskClouds = function(image) {
var cs = cloud_shadows(image);
var c = clouds(image);
image = image.updateMask(cs);
return image.updateMask(c);
};
// --------- Input: ------------------------------------
var startDate = ee.Date('2018-12-31') //2016-09-15');
var endDate = ee.Date('2021-06-01');
var AOI =
ee.Geometry.Polygon(
[[[140.88237590701584,-37.896469860299604],
[140.96546001346115,-37.896469860299604],
[140.96546001346115,-37.83819826191272],
[140.88237590701584,-37.83819826191272],
[140.88237590701584,-37.896469860299604]]], null, false);
/* [[[140.5710269570096,-37.669974265519755],
[140.64037815329866,-37.669974265519755],
[140.64037815329866,-37.60037237657578],
[140.5710269570096,-37.60037237657578],
[140.5710269570096,-37.669974265519755] */
//Map.addLayer(AOI, {}, 'AOI', true);
Map.centerObject(AOI, 13);
var imageStartDate1 = startDate.advance(-30,"day");
var imageStartDate2 = startDate.advance(30,"day");
var imageEndDate1 = endDate.advance(-30,"day");
var imageEndDate2 = endDate.advance(30,"day");
var imagery = ee.ImageCollection("COPERNICUS/S2_SR");
//S2-SR: COPERNICUS/S2_SR //LANDSAT/LC08/C01/T1_SR
var Sen2 = ee.ImageCollection(imagery
// Filter by dates.
.filterDate(imageStartDate1, imageStartDate2)
// Filter by location.
.filterBounds(AOI));
var Sen2end = ee.ImageCollection(imagery
// Filter by dates.
.filterDate(imageEndDate1, imageEndDate2)
// Filter by location.
.filterBounds(AOI));
var Sen2mosaic = Sen2.mosaic().clip(AOI);
var Sen2mosaicEnd = Sen2end.mosaic().clip(AOI);
//print("Sen2mosaic", Sen2mosaic);
var composite_free = Sen2end.map(maskClouds);
var visParams = {bands:['B8', 'B11', 'B4'], min:0, max:5000};
Map.addLayer(composite_free.first(), visParams, 'cloud mask');
// Create the NDVI and NDWI spectral indices.
var ndvi = Sen2mosaic.normalizedDifference(['B8', 'B4']);
var ndwi = Sen2mosaic.normalizedDifference(['B3', 'B8']);
var ndviEnd = Sen2mosaicEnd.normalizedDifference(['B8', 'B4']);
var ndwiEnd = Sen2mosaicEnd.normalizedDifference(['B3', 'B8']);
// Create some binary images from thresholds on the indices.
// This threshold is designed to detect bare land.
var bare1 = ndvi.lt(0.2).and(ndwi.lt(0.3));
var bare1End = ndviEnd.lt(0.2).and(ndwiEnd.lt(0.3));
// This detects bare land with lower sensitivity. It also detects shadows.
var bare2 = ndvi.lt(0.2).and(ndwi.lt(0.8));
var bare2End = ndviEnd.lt(0.2).and(ndwiEnd.lt(0.8));
// Define visualization parameters for the spectral indices.
var ndviViz = {min: -1, max: 1, palette: ['FF0000', '00FF00']};
var ndwiViz = {min: 0.5, max: 1, palette: ['00FFFF', '0000FF']};
// Mask and mosaic visualization images. The last layer is on top.
var thematic = ee.ImageCollection([
// NDWI > 0.5 is water. Visualize it with a blue palette.
ndwi.updateMask(ndwi.gte(0.5)).visualize(ndwiViz),
// NDVI > 0.2 is vegetation. Visualize it with a green palette.
ndvi.updateMask(ndvi.gte(0.2)).visualize(ndviViz),
// Visualize bare areas with shadow (bare2 but not bare1) as gray.
bare2.updateMask(bare2.and(bare1.not())).visualize({palette: ['AAAAAA']}),
// Visualize the other bare areas as white.
bare1.updateMask(bare1).visualize({palette: ['FFFFFF']}),
]).mosaic();
var thematicEnd = ee.ImageCollection([
ndwiEnd.updateMask(ndwiEnd.gte(0.5)).visualize(ndwiViz),
ndviEnd.updateMask(ndviEnd.gte(0.2)).visualize(ndviViz),
bare2End.updateMask(bare2End.and(bare1.not())).visualize({palette: ['AAAAAA']}),
bare1End.updateMask(bare1End).visualize({palette: ['FFFFFF']}),
]).mosaic();
//Map.addLayer(thematic, {}, 'thematic', false);
//Map.addLayer(thematicEnd, {}, 'thematic end', false);
//Map.addLayer(ndvi, {}, 'NDVI', false);
//Map.addLayer(ndviEnd, {}, 'NDVI end', false);
var Band_R = Sen2mosaic.expression('RED / count', {'RED': Sen2mosaic.select('B4'), count : Sen2.size()});
var Band_G = Sen2mosaic.expression('GREEN / count', {'GREEN': Sen2mosaic.select('B3'), count : Sen2.size()});
var Band_B = Sen2mosaic.expression('BLUE / count', {'BLUE': Sen2mosaic.select('B2'), count : Sen2.size()});
var Band_Rend = Sen2mosaicEnd.expression('RED / count', {'RED': Sen2mosaicEnd.select('B4'), count : Sen2.size()});
var Band_Gend = Sen2mosaicEnd.expression('GREEN / count', {'GREEN': Sen2mosaicEnd.select('B3'), count : Sen2.size()});
var Band_Bend = Sen2mosaicEnd.expression('BLUE / count', {'BLUE': Sen2mosaicEnd.select('B2'), count : Sen2.size()});
var image_RGB = Band_R.addBands(Band_G).addBands(Band_B);
var image_RGBend = Band_Rend.addBands(Band_Gend).addBands(Band_Bend);
var image_viz_params = {
//'bands': ['B5', 'B4', 'B3'],
'min': 2,
'max': 240,
gamma: 1.5,
//'gamma': [0.95, 1.1, 1]
};
Map.addLayer(image_RGB, image_viz_params, 'RGB 12/2018', false);
Map.addLayer(image_RGBend, image_viz_params, 'RGB 12/2020', false);
var dNDVI = ndviEnd.subtract(ndvi);
// Scale product to USGS standards
var dNDVIscaled = dNDVI.multiply(1000);
// Add the difference image to the console on the right
print("Difference Normalized Difference Vegetation Index: ", dNDVI);
//--------------------------- NDVI Product - classified -------------------------------
// Define an SLD style of discrete intervals to apply to the image.
var sld_intervals =
'<RasterSymbolizer>' +
'<ColorMap type="intervals" extended="false" >' +
'<ColorMapEntry color="#ff1b1b" quantity="-250" label="-250" />' +
'<ColorMapEntry color="#ffa81b" quantity="-100" label="-100" />' +
'<ColorMapEntry color="#f5ff1b" quantity="250" label="250" />' +
'<ColorMapEntry color="#1bff2f" quantity="500" label="500" />' +
'<ColorMapEntry color="#099b16" quantity="1000" label="1000" />' +
'</ColorMap>' +
'</RasterSymbolizer>';
Map.addLayer(dNDVIscaled.sldStyle(sld_intervals), {}, 'dNDVI classified');
//==========================================================================================
// ADD A LEGEND
// set position of panel
var legend = ui.Panel({
style: {
position: 'bottom-left',
padding: '8px 15px'
}});
// Create legend title
var legendTitle = ui.Label({
value: 'dNDVI Classes',
style: {fontWeight: 'bold',
fontSize: '18px',
margin: '0 0 4px 0',
padding: '0'
}});
// 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 palette =['ff1b1b', 'ffa81b', 'f5ff1b', '1bff2f', '099b16', 'ffffff'];
// name of the legend
var names = ['Forest Loss, High Probability', 'Forest Loss', 'Unchanged', 'Forest Gain, Low', 'Forest Gain, High Probability'];
// Add color and and names
for (var i = 0; i < 5; i++) {
legend.add(makeRow(palette[i], names[i]));
}
// add legend to map (alternatively you can also print the legend to the console)
Map.add(legend);
// Export a cloud-optimized GeoTIFF.
Export.image.toDrive({
image: dNDVIscaled,
description: 'imageToCOGeoTiffExample',
scale: 10,
region: AOI,
fileFormat: 'GeoTIFF',
formatOptions: {
cloudOptimized: true
}
});
https://code.earthengine.google.com/586a1c0df85e74d4df8871618a965f6a

How to limit xAxis label in Chart.Js 1.0.2?

I am trying to plot a twitter streaming ,tweet sentiment score on per minute basis using ChartJs 1.0.2 .The xlables are getting overlapped after sometime. How can i plot the graph for a limited number of xAxis Labels to avoid the overlap. I am using Apache Zeppelin and AngularJs interpreter for the plotting. The code is as follows.
lineChartData = {}; //declare an object
lineChartData.labels = []; //add 'labels' element to object (X axis)
lineChartData.datasets = []; //add 'datasets' array element to object
//FIRST GRAPH: score
var line = 0
y = [];
lineChartData.datasets.push({}); //create a new line dataset
dataset = lineChartData.datasets[line]
dataset.fillColor = "rgba(0, 0, 0, 0)";
dataset.strokeColor = "rgba(75,192,192,0.4)";
dataset.lineColor = "rgba(75,192,192,0.4)";
dataset.label = "Score"
dataset.data = []; // value data
angular.forEach(newValue, function(x) {
y.push(x._3);
if (line === 0)
lineChartData.labels.push(x._2); //adds x axis labels
})
lineChartData.datasets[line].data = y; //send new line data to dataset
//end FIRST GRAPH: score
var options = {
responsive: true,
animation: false,
multiTooltipTemplate: function(dataset) {
//console.log(dataset)
return dataset.datasetLabel+ " : " + dataset.value ;
dataset.strokeColor='red';
} ,
}
ctx = canvas.getContext("2d");
myLineChart = new Chart(ctx).Line(lineChartData, options);
var legend = myLineChart.generateLegend();
$("#legendDiv").html(legend);
//document.getElementById("legendDiv").innerHTML = legend;
})
}
if (window.L) {
initMap();
} else {
console.log('Loading Leaflet library');
var sc = document.createElement('script');
sc.type = 'text/javascript';
sc.src = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js';
sc.onload = initMap;
sc.onerror = function(err) { alert(err); }
document.getElementsByTagName('head')[0].appendChild(sc);
}
I can think of following ways.
Get only the data for limited time window to the variable but not get all data.
Group the data to large time point
Example the data for certain interval
BTW, It might be worth to wrap the code which convert to data to visualisation. It will be easier to change to a different chart or use different plot option.
If you don't have strong preference for chart.js, check spark-highcharts to see if it meets your plot needs, or create a similar wrapper to chart.js.

Passing an array to feed svg.js's polyline array method throws an error

I'm using svg.js's polyline array method to draw lines from values logged by storing the cursor's x,y coordinates on a page.
The idea is to first let a user browsing the page and move the cursor around, and so feed values into the array, then when clicking on a button svg.js will draw lines based on the cursor' movement.
Being this the syntax to follow:
var polyline = draw.polyline([[0,0], [100,50], [50,100]]).fill('none').stroke({ width: 1 })
if I manually copy-paste the values in the array as in:
var polyline = draw.polyline([[475,76], [475,76], [489,75], (...)]]).fill('none').stroke({ width: 1 });
svg.js works as expected.
If I pass in a variable, it tells me this:
svg.js:2632 Error: <polyline> attribute points: Expected number, "NaN,528 NaN,531 …".
This is my code:
// movement
var array_cursor_movement = [];
$(document).mousemove(function(e) {
var cursor_movement = "["+ e.pageX +","+ e.pageY +"]";
array_cursor_movement.push(cursor_movement);
var cursor_movement = array_cursor_movement.join(', ');
console.log("movement:" + cursor_movement);
localStorage.setItem("cursor_movement_pos", JSON.stringify(array_cursor_movement));
});
// click for preview lines
$(".preview--lines, .close--button").click(function(){
$(".preview").toggleClass("show--hide");
// draw lines
var retrievedData = localStorage.getItem("cursor_movement_pos");
var array_cursor_movement_b = JSON.parse(retrievedData);
var cursor_movement = array_cursor_movement_b.join(', ');
cursor_movement = "["+cursor_movement+"]";
console.log("preview:" + cursor_movement);
var width = window.innerWidth;
var height = window.innerHeight;
var draw = SVG('drawing').size(width, height);
var polyline = draw.polyline(cursor_movement).fill('none').stroke({ width: 1 });
})
Anyone has experience?
Thanks,
André
As I already mentioned in the comments: Stop making Strings!! Just pass the array!
// movement
var array_cursor_movement = [];
$(document).mousemove(function(e) {
// var cursor_movement = "["+ e.pageX +","+ e.pageY +"]"; // NO - stop making strings!
var cursor_movement = [ e.pageX, e.pageY ];
array_cursor_movement.push(cursor_movement);
// var cursor_movement = array_cursor_movement.join(', '); // STOP
console.log("movement:", cursor_movement);
localStorage.setItem("cursor_movement_pos", JSON.stringify(array_cursor_movement));
});
// click for preview lines
$(".preview--lines, .close--button").click(function(){
$(".preview").toggleClass("show--hide");
// draw lines
var retrievedData = localStorage.getItem("cursor_movement_pos");
var array_cursor_movement_b = JSON.parse(retrievedData);
// var cursor_movement = array_cursor_movement_b.join(', '); // what did I say?
// cursor_movement = "["+cursor_movement+"]";
// console.log("preview:", cursor_movement);
var width = window.innerWidth;
var height = window.innerHeight;
var draw = SVG('drawing').size(width, height);
// just pass the array and not the string
var polyline = draw.polyline(array_cursor_movement_b).fill('none').stroke({ width: 1 });
})
PS: Corrected Codepen: http://codepen.io/anon/pen/dMjgdO
// EDIT: I guess I make things more clear:
As you said yourself the syntax to create a polyline is as follows:
var polyline = draw.polyline([[0,0], [100,50], [50,100]]).fill('none').stroke({ width: 1 })
But you tried to do this instead:
var polyline = draw.polyline("[[0,0], [100,50], [50,100]]").fill('none').stroke({ width: 1 })
^ you notice the quotes here?
So you did not pass an array to the function. You passed a string which looks like an array.
Note that there is another syntax to create a polyline which is:
var polyline = draw.polyline('0,0 100,50 50,100').fill('none').stroke({ width: 1 })
So you actually CAN pass a string. It just is a comma and space seperated list. But DONT do that. You already created your array. You dont need the string.

How to set the maximum zoom level dynamically to fit a list of markers in google-map?

This is my code, I've used angular-google-map API for it. Here I try to set the zoom level of 20 statically, but as per my requirement, in some place I need 17, somewhere 18 zoom level. So I want to dynamically generate the maximum possible zoom level to fit all the markers.
var deviceCords = [];
var bounds = new google.maps.LatLngBounds();
$scope.$parent.$parent.$parent.map.zoom = 20;
var devicePromise = SiteService.getDevicesForSite(marker).$promise;
devicePromise.then(function(deviceData){
var markerCategory = marker.category;
if(markerCategory === "Public Venues")
markerCategory = "Venue";
else if(markerCategory === "Public Transit")
markerCategory = "Trans";
$scope.$parent.$parent.$parent.categoryFilter(markerCategory);
var devices = deviceData.devices;
var markers = $scope.$parent.$parent.$parent.markers;
markers.splice(0, markers.length);
angular.forEach(devices, function(device){
deviceCords.push(new google.maps.LatLng(device.latitude, device.longitude));
var deviceMarker = {
id : device.deviceId,
name : device.name,
latitude : device.latitude,
longitude: device.longitude,
category : device.category,
icon: "assets/images/device.png"
};
markers.push(deviceMarker);
});
angular.forEach(deviceCords, function(deviceCord){
bounds.extend(deviceCord);
});
var latlng = bounds.getCenter();
$scope.$parent.$parent.$parent.map.center = {
latitude: latlng.lat().toString().substr(0, 20),
longitude: latlng.lng().toString().substr(0, 20)
};
$scope.$parent.$parent.$parent.map.fitBounds(bounds);
Remove the zoom level. It will automatically adjust the value such that all markers are covered. Read this section - https://developers.google.com/maps/documentation/static-maps/intro#Markers

Creating an javascript graph with marker that is synchronize with a movie

I'm trying to create an online web tool for eeg signal analysis. The tool suppose to display a graph of an eeg signal synchronize with a movie that was display to a subject.
I've already implemented it successfully on csharp but I can't find a way to do it easily with any of the know javascript chart that I saw.
A link of a good tool that do something similar can be found here:
http://www.mesta-automation.com/real-time-line-charts-with-wpf-and-dynamic-data-display/
I've tried using dygraph, and google chart. I know that it should be relatively easy to create an background thread on the server that examine the movie state every ~50ms. What I was not able to do is to create a marker of the movie position on the chart itself dynamically. I was able to draw on the dygraph but was not able to change the marker location.
just for clarification, I need to draw a vertical line as a marker.
I'm in great suffering. Please help :)
Thanks to Danvk I figure out how to do it.
Below is a jsfiddler links that demonstrate such a solution.
http://jsfiddle.net/ng9vy8mb/10/embedded/result/
below is the javascript code that do the task. It changes the location of the marker in synchronizing with the video.
There are still several improvement that can be done.
Currently, if the user had zoomed in the graph and then click on it, the zoom will be reset.
there is no support for you tube movies
I hope that soon I can post a more complete solution that will also enable user to upload the graph data and video from their computer
;
var dc;
var g;
var v;
var my_graph;
var my_area;
var current_time = 0;
//when the document is done loading, intialie the video events listeners
$(document).ready(function () {
v = document.getElementsByTagName('video')[0];
v.onseeking = function () {
current_time = v.currentTime * 1000;
draw_marker();
};
v.oncanplay = function () {
CreateGraph();
};
v.addEventListener('timeupdate', function (event) {
var t = document.getElementById('time');
t.innerHTML = v.currentTime;
g.updateOptions({
isZoomedIgnoreProgrammaticZoom: true
});
current_time = v.currentTime * 1000;
}, false);
});
function change_movie_position(e, x, points) {
v.currentTime = x / 1000;
}
function draw_marker() {
dc.fillStyle = "rgba(255, 0, 0, 0.5)";
var left = my_graph.toDomCoords(current_time, 0)[0] - 2;
var right = my_graph.toDomCoords(current_time + 2, 0)[0] + 2;
dc.fillRect(left, my_area.y, right - left, my_area.h);
};
//data creation
function CreateGraph() {
number_of_samples = v.duration * 1000;
// A basic sinusoidal data series.
var data = [];
for (var i = 0; i < number_of_samples; i++) {
var base = 10 * Math.sin(i / 90.0);
data.push([i, base, base + Math.sin(i / 2.0)]);
}
// Shift one portion out of line.
var highlight_start = 450;
var highlight_end = 500;
for (var i = highlight_start; i <= highlight_end; i++) {
data[i][2] += 5.0;
}
g = new Dygraph(
document.getElementById("div_g"),
data, {
labels: ['X', 'Est.', 'Actual'],
animatedZooms: true,
underlayCallback: function (canvas, area, g) {
dc = canvas;
my_area = area;
my_graph = g;
bottom_left = g.toDomCoords(0, 0);
top_right = g.toDomCoords(highlight_end, +20);
draw_marker();
}
});
g.updateOptions({
clickCallback: change_movie_position
}, true);
}

Categories

Resources