Google Earth Engine – How to Get Image Value on Click?

google-earth-engine

When clicking on a GEE map, I would like to get the value of an Image's (1-band raster) pixel.

I'm using Map.onClick to extract values from point on a map. According to GEE examples (e.g. https://developers.google.com/earth-engine/tutorial_api_03), xx.reduceRegion(ee.Reducer.mean(), clickpoint, scale) should work for ImageCollection objects but does not seem to work for Image objects.

// Load 1-band image
var dataset = ee.Image('WWF/HydroSHEDS/03VFDEM').select('b1');
print(dataset);
Map.style().set('cursor', 'crosshair');
Map.addLayer(dataset);

// Following is adapted from https://code.earthengine.google.com/?scriptPath=Examples:User+Interface/Ocean+Timeseries+Investigator
// Set a callback function for when the user clicks the map.
var header = ui.Label('Some text', {fontWeight: 'bold', fontSize: '18px'});
var toolPanel = ui.Panel([header], 'flow', {width: '400px'});

Map.onClick(function(coords) {
  // Create or update the location label (the second widget in the panel)
  var location = 'lon: ' + coords.lon.toFixed(4) + ' ' +
                 'lat: ' + coords.lat.toFixed(4);
  var click_point = ee.Geometry.Point(coords.lon, coords.lat);
  var demValue = dataset.reduceRegion(ee.Reducer.first(), click_point, 90);
  var demText = 'Habitat suitability: ' + demValue;
// var demText = 'Habitat suitability: ' + demValue.get('b1');
  toolPanel.widgets().set(1, ui.Label(location));
  toolPanel.widgets().set(2, ui.Label(demText));

  // Add a red dot to the map where the user clicked.
  Map.layers().set(1, ui.Map.Layer(click_point, {color: 'FF0000'}));

});

// Add the panel to the ui.root.
ui.root.add(toolPanel);

Using demValue directly or demValue.get('b1') doesn't change anything.

The resulting panel gives (-86.6746,35.3036 being a random point):

Some text

lon: -86.6746 lat: 35.3036

Habitat suitability:
ee.Dictionary({ "type": "Invocation", "arguments": { "image": {
"type": "Invocation", "arguments": { "input": { "type": "Invocation",
"arguments": { "id": "WWF/HydroSHEDS/03VFDEM" }, "functionName":
"Image.load" }, "bandSelectors": [ "b1" ] }, "functionName":
"Image.select" }, "reducer": { "type": "Invocation", "arguments": {},
"functionName": "Reducer.first" }, "geometry": { "type": "Point",
"coordinates": [ -86.67460937499999, 35.30364788916715 ] }, "scale":
90 }, "functionName": "Image.reduceRegion" })

GEE script: https://code.earthengine.google.com/1204e25cbbdb782d9af4c7944d047bf2

Best Answer

To add to @Rodrigo's answer. You definitely need to bring the data to client side before you can display it on your UI. BUT since you are dealing with UI, you definitely don't want the application to freeze when you click on map. So it might be a better idea to use the evaluate function instead which will run the data fetching process in the background without stopping the UI functions. You can even temporarily set the label as something like loading... An implementation of it within the onClick function could be

Map.onClick(function(coords) {
  var location = 'lon: ' + coords.lon.toFixed(4) + ' ' +
                 'lat: ' + coords.lat.toFixed(4);
  var click_point = ee.Geometry.Point(coords.lon, coords.lat);
  var demValue = dataset.reduceRegion(ee.Reducer.first(), click_point, 90).evaluate(function(val){
    var demText = 'Habitat suitability: ' + val.b1;
    toolPanel.widgets().set(2, ui.Label(demText));
  });
  toolPanel.widgets().set(1, ui.Label(location));
// Edit: To be temporary, the "loading..." panel number has to be the same as the demText panel number (changed from 1 to 2).
  toolPanel.widgets().set(2, ui.Label("loading..."));
  Map.layers().set(1, ui.Map.Layer(click_point, {color: 'FF0000'}));
});
Related Question