I'm working on a map view that detects new ice using Sentinel-1 together with Google Earth Engine.

When adding images to Google Earth Engine I can set the min and max values for my layer in numbers but then in the map view I have the possibility to change change these values and also select different stretch options.

visualization parameters

Is it possible to choose one of these options instead of entering a max and min view in the code? Right now my code end looks like this:

Map.addLayer(hh2017, {

bands: ["HH"],
max: -5,
min: -19,
opacity: 1,
palette: ['002bff','a4d5ff','979797','080358','000029' ]

}, 'sentinel-1');

I want it to be Stretch: 90% The reason I need to stretch the image each time is that they all look a bit different.


As described here, you may want to try a Styled Layer Descriptor. The previous answer also works, but requires you to use getInfo(), which can cause your browser to lock (see Client-Server doc for details). Here's another approach:

var i = ee.Image(ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA')

function setDisplay(dictionary) {
  var visParams = {
    bands: ['B4', 'B3', 'B2'], 
    min: [
      dictionary['B4_p5'], dictionary['B3_p5'], dictionary['B2_p5']
    max: [
      dictionary['B4_p95'], dictionary['B3_p95'], dictionary['B2_p95']
  Map.layers().set(0, ui.Map.Layer(i, visParams));

Map.onChangeBounds(function() {
  var params = i.reduceRegion({
    reducer: ee.Reducer.percentile([5, 95]), 
    geometry: Map.getBounds(true), 
    scale: Map.getScale(),
  • 1
    That saves a lot of typing, very cool. You aren't winning in your crusade to banish getInfo :-) Oct 25 '17 at 8:31
  • I like this approach, mainly because it does the job in the current display as the user changes bounds, but as I see it, this solution doesn't avoid the Client-Server protocol, it just retrieves information asynchronously using evaluate whether getInfo does it synchronously. Sure in this case is better to use evaluate Oct 25 '17 at 16:42

For stretching using the mean and standard deviation I wrote a function available in https://github.com/gee-community/gee_tools. I do not know how GEE does it when you do it as you mention (developers like as Noel Gorelick may know), but somehow it has slightly different values, see the test.

var stretch_std = function(n_std) {
  var mean = i.reduceRegion({
    reducer: ee.Reducer.mean(),
    geometry: i.geometry(),
    bestEffort: true})
  var std = i.reduceRegion({
    reducer: ee.Reducer.stdDev(),
    geometry: i.geometry(),
    bestEffort: true})
  var min = mean.map(function(key, val){
      return ee.Number(val).subtract(ee.Number(std.get(key)).multiply(n_std))
  var max = mean.map(function(key, val){
      return ee.Number(val).add(ee.Number(std.get(key)).multiply(n_std))

  return {vmin: min, vmax: max}

var i = ee.Image("LANDSAT/LC8_L1T_TOA_FMASK/LC82310902013296LGN00")
var s = stretch_std(3)
var min = ee.Number(ee.List([s.vmin.B5, s.vmin.B6, s.vmin.B4]).reduce(ee.Reducer.mean()))
var max = ee.Number(ee.List([s.vmax.B5, s.vmax.B6, s.vmax.B4]).reduce(ee.Reducer.mean()))
var viz = {bands:["B5", "B6", "B4"],
           min: min.getInfo(),
           max: max.getInfo()}
var vizEE = {bands:["B5", "B6", "B4"],
           min: -0.06102305441591904,
           max: 0.41922686174360246}
Map.addLayer(i, {bands:["B5", "B6", "B4"],
                 min:0, max:1}, "not streched")
Map.addLayer(i, vizEE, "streched by EE")
Map.addLayer(i, viz, "streched by gee_tools")

If you want to stretch based on statistics for a fixed region, you can use the following approach, which demonstrates both percentage and standard deviation based stretches.

// Function for displaying stretched image based on standard devitations from the mean.
var AddLayerStdDevStretch = function(img, sampling_geom, num_stddev) {
  var stats = img.select(['elevation'],['value']).reduceRegion({
    reducer: ee.Reducer.mean().combine({reducer2:ee.Reducer.stdDev(), sharedInputs:true})
                              .setOutputs(['mean', 'stddev']), 
    geometry: sampling_geom,
    scale: 30, 
    bestEffort: true
  var vis_params = ee.Dictionary({
      'min': ee.Number(stats.get('value_mean'))
      'max': ee.Number(stats.get('value_mean'))
    eeObject: img,
    visParams: vis_params.getInfo(),
    name: num_stddev + ' sigma'

// Function for displaying stretched image based on percentile values.
var AddLayerPercentStretch = function(img, sampling_geom, percent) {
  var lower_percentile = ee.Number(100).subtract(percent).divide(2);
  var upper_percentile = ee.Number(100).subtract(lower_percentile);
  var stats = img.select(['elevation'],['value']).reduceRegion({
    reducer: ee.Reducer.percentile({percentiles: [lower_percentile, upper_percentile]}).setOutputs(['lower', 'upper']),
    geometry: sampling_geom,
    scale: 30, 
    bestEffort: true
  var vis_params = ee.Dictionary({
      'min': ee.Number(stats.get('value_lower')),
      'max': ee.Number(stats.get('value_upper'))
    eeObject: img,
    visParams: vis_params.getInfo(),
    name: percent + '%'

// Sample use - add layers with various stretches to the map.
var image = ee.Image("USGS/SRTMGL1_003");
var geometry = ee.Geometry.Polygon(
        [[[-112.6, 36.3],
          [-112.6, 35.9],
          [-111.9, 35.9],
          [-111.9, 36.3]]]);
AddLayerStdDevStretch(image, geometry, 1);
AddLayerStdDevStretch(image, geometry, 2);
AddLayerStdDevStretch(image, geometry, 3);
AddLayerPercentStretch(image, geometry, 90);
AddLayerPercentStretch(image, geometry, 98);
AddLayerPercentStretch(image, geometry, 100);
Map.addLayer(geometry, {}, 'geometry used for stretch statistics');

Note that the percentage based stretches are based on a histogram (see ee.Reducer.percentile), so the 100% stretch may not exactly match the extreme values within the sampling region.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.