[GIS] Trying to make a histogram with ui.Chart.image.histogram() in GEE

errorgoogle-earth-enginehistogram

I'm trying to make a histogram with the difference of two NDVI images from different dates, but I'm having trouble with the ui.Chart.image.histogram() function when I try to perform with my large asset (https://code.earthengine.google.com/?asset=users/felolillorobles/Araucania_ODEPA_2019_Buffer) to obtain the histogram, equivalent to 46881041 pixels in the region.

The Error is the following:

Error generating chart: Image.reduceRegion: Too many pixels in the region. Found 46881041, but maxPixels allows only 10000000.

The error suggests I set MaxPixels to a limit suitable for my computation, or set BestEffort to true. However, when I do this, I must change my original variable (ee.Image) to a dictionary using a reducer. This throws another error when I try to pass this new reduced variable (ee.Dictionary) as an input to ui.Chart.image.histogram(), because the function expects an ee.Image (like my original variable).

Another way that I've been trying this is with an ee.Reducer.histogram(), which allows me to use my large asset, but then, I don't know how to graph with the values contained in the resulting dictionary. Is possible to pass this dictionary object to an ui.Chart.image.histogram()?

Here is my script, the characters in // are other options that i was trying:

// CALCULO DE DNDVI PARA SENTINEL 2A SR

//Imagen Sentinel 2A en la fecha 1
var IMGSentinel2A= ee.ImageCollection("COPERNICUS/S2")
.filterDate ('2016-10-01', '2017-03-31')
.filterBounds (geometry)
.filterMetadata ("CLOUDY_PIXEL_PERCENTAGE",'Less_Than', 5);
var Sentinel2filtro = ee.Image(IMGSentinel2A.max());
var Sentinel2Clip = Sentinel2filtro.clip (geometry);


//BANDAS FECHA 1
var b2_1 = Sentinel2Clip.select('B2');
var b3_1 = Sentinel2Clip.select('B3');
var b4_1 = Sentinel2Clip.select('B4');
var b5_1 = Sentinel2Clip.select('B5');
var b8_1 = Sentinel2Clip.select('B8');
var b11_1 = Sentinel2Clip.select('B11');
var b12_1 = Sentinel2Clip.select('B12');

//Imagen Sentinel 2A en la fecha 2
var IMGSentinel2A2= ee.ImageCollection("COPERNICUS/S2")
.filterDate ('2018-10-01', '2019-03-31')
.filterBounds (geometry)
.filterMetadata ("CLOUDY_PIXEL_PERCENTAGE",'Less_Than', 5);
// .filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 10));
 var Sentinel2filtro2 = ee.Image(IMGSentinel2A2.median());
 var Sentinel2Clip2 = Sentinel2filtro2.clip (geometry);

//BANDAS FECHA 2
var b2_2 = Sentinel2Clip2.select('B2'); 
var b3_2 = Sentinel2Clip2.select('B3'); 
var b4_2 = Sentinel2Clip2.select('B4'); 
var b5_2 = Sentinel2Clip2.select('B5'); 
var b8_2 = Sentinel2Clip2.select('B8'); 
var b11_2 = Sentinel2Clip2.select('B11'); 
var b12_2 = Sentinel2Clip2.select('B12'); 


//Normalized differences
var normdif1 = Sentinel2Clip.normalizedDifference(['B8', 'B4']);
var normdif2 = Sentinel2Clip2.normalizedDifference(['B8', 'B4']);
var dndvi = normdif1.subtract(normdif2);
print (dndvi)

//   var hist = dndvi.reduceRegion({
//   reducer: ee.Reducer.histogram(),
//   geometry: geometry.geometry(),
//   scale: 10,
//   bestEffort: true,
// });

var hist = dndvi.reduceRegion({
 reducer: ee.Reducer.mean(),
geometry: geometry.geometry(),
scale: 10,
bestEffort: true,
 });

 print(hist)

 // Pre-define some customization options.

var options = {
  title: 'HISTOGRAMA dNDVI',
  fontSize: 20,
  hAxis: {title: 'banda'},
  vAxis: {title: 'Frecuencia'},
  series: {
    0: {color: 'blue'},
}};
// Make the histogram, set the options.
var histogram = ui.Chart.image.histogram(hist, geometry, 30)
    .setSeriesNames(['dNDVI'])
    .setOptions(options);
    // Display the histogram.
print(histogram);

Best Answer

Below is a conceptual example, based on your analysis, for generating histograms by region reduction and charting the results using either ee.Array or ee.FeatureCollection objects. I prefer these methods over charting by image histogram because they are more flexible, and in the case of the ee.FeatureCollection option, the histogram table can be exported as an asset, which can help when in-browser processing times out, allowing you to make histogram charts from very large regions at small scales.

Code Editor script

/**
 * Copyright 2020 Google LLC.
 * SPDX-License-Identifier: Apache-2.0
 */

// Define a region of interest.
var geometry = ee.Geometry.Polygon(
  [[[-73.16191843371962, -37.8098361104156],
    [-73.16191843371962, -39.593041263040206],
    [-71.14043405871962, -39.593041263040206],
    [-71.14043405871962, -37.8098361104156]]], null, false);

// Display the region of interest.
Map.centerObject(geometry);
Map.addLayer(geometry);

// Filter image collection 1 and reduce to max.
var sentinel2a = ee.ImageCollection("COPERNICUS/S2")
  .filterDate('2016-10-01', '2017-03-31')
  .filterBounds(geometry)
  .filterMetadata("CLOUDY_PIXEL_PERCENTAGE",'Less_Than', 5)
  .max();

// Filter image collection 2 and reduce to median.
var sentinel2b = ee.ImageCollection("COPERNICUS/S2")
  .filterDate('2018-10-01', '2019-03-31')
  .filterBounds(geometry)
  .filterMetadata("CLOUDY_PIXEL_PERCENTAGE",'Less_Than', 5)
  .median();

// Calculate difference between two dates of normalized difference (DNDVI).
var normDif1 = sentinel2a.normalizedDifference(['B8', 'B4']);
var normDif2 = sentinel2b.normalizedDifference(['B8', 'B4']);
var dndvi = normDif1.subtract(normDif2);

// Calculate a histogram of DNDVI using a region reduction with the
// `autoHistogram()` reducer. Note that scale is set to 1000 so that the
// operation will not time out while interactive in the Code Editor browser.
// If you have a large region and a small scale, see the below section on
// exporting the result as an asset to avoid time out errors.
var hist = dndvi.reduceRegion({
  reducer: ee.Reducer.autoHistogram(),
  geometry: geometry,
  scale: 1000,
  bestEffort: true,
});

// #############################################################################
// ### HISTOGRAM CHART USING ARRAYS ###
// #############################################################################

// The result of the region reduction by `autoHistogram` is an array. Get the
// array and cast it as such for good measure.
var histArray = ee.Array(hist.get('nd'));

// Subset the values that represent the bottom of the bins and project to
// a single dimension. Result is a 1-D array.
var binBottom = histArray.slice(1, 0, 1).project([0]);

// Subset the values that represent the number of pixels per bin and project to
// a single dimension. Result is a 1-D array.
var nPixels = histArray.slice(1, 1, null).project([0]);

// Chart the two arrays using the `ui.Chart.array.values` function.
var histColumnFromArray = ui.Chart.array.values({
  array:nPixels,
  axis: 0,
  xLabels: binBottom})
  .setChartType('ColumnChart');
print(histColumnFromArray);


// #############################################################################
// ### HISTOGRAM CHART USING A FEATURE COLLECTION ###
// #############################################################################

// Cast the histogram table array as a list.
var histList = histArray.toList();

// Map over the list to create a list of features per bin and set respective
// bin bottom and number of pixels properties.
var featureList = histList.map(function(bin) {
  bin = ee.List(bin);
  var props = {
    binBottom: ee.Number(bin.get(0)),
    nPixels: ee.Number(bin.get(1))
  };
  return ee.Feature(geometry, props);
});

// Convert the feature list to a feature collection.
var featureCol = ee.FeatureCollection(featureList);

// #############################################################################
// OPTIONALLY EXPORT THE FEATURE COLLECTION

// If your region reduction times out in the browser, export the above
// feature collection as an asset. When it completes, start a new script
// that imports it and then chart it using the options following. Here is
// an example of exporting the region as an asset:

Export.table.toAsset({
  collection: featureCol,
  description: 'histogram_table',
  assetId: 'histogram_table'
});

// #############################################################################

// Chart histogram from the constructed feature collection as a line chart.
var histLineFromFc = ui.Chart.feature.byFeature({
  features: featureCol,
  xProperty: 'binBottom',
  yProperties: ['nPixels']})
  .setChartType('LineChart');
print(histLineFromFc);

// Chart histogram from the constructed feature collection as a column chart.
var histColumnFromFc = ui.Chart.feature.byFeature({
  features: featureCol,
  xProperty: 'binBottom',
  yProperties: ['nPixels']})
  .setChartType('ColumnChart');
print(histColumnFromFc);
Related Question