Google Earth Engine Python API – ee.Image.sample Not Working

google-earth-engine-python-api

I am using ee and geemap in a conda environment running Python 3.9.16 and am trying to sample a raster, i.e. convert image pixels to features. This is a documentation example translated into Python.
Unfortunately, no matter what geometry is used, an error is thrown

EEException: Invalid GeoJSON geometry.

Using sampleRegion(...) instead I am getting

EEException: Unrecognized argument type to convert to a FeatureCollection: {'region': , 'geometries': True}

What is causing the problem?

import ee
import geemap
ee.Authenticate()
ee.Initialize()

# Demonstrate extracting pixels from an image as features with
# ee.Image.sample(), and show how the features are aligned with the pixels.

# An image with one band of elevation data.
image = ee.Image('CGIAR/SRTM90_V4')
VIS_MIN = 1620
VIS_MAX = 1650
Map.addLayer(image, {'min': VIS_MIN, 'max': VIS_MAX}, 'SRTM')

# Region to sample.
region = ee.Geometry.Polygon(
  [[[-110.006, 40.002],
    [-110.006, 39.999],
    [-109.995, 39.999],
    [-109.995, 40.002]]], None, False)
# Show region on the map.
Map.setCenter(-110, 40, 16)
Map.addLayer(ee.FeatureCollection([region]).style(**{"color": "00FF0022"}))

# Perform sampling; convert image pixels to features.
samples = image.sample({
  'region': region,

  # Default (False) is no geometries in the output.
  # When set to True, each feature has a Point geometry at the center of the
  # image pixel.
  'geometries': True,

  # The scale is not specified, so the resolution of the image will be used,
  # and there is a feature for every pixel. If we give a scale parameter, the
  # image will be resampled and there will be more or fewer features.
  #
  # scale: 200,
})

# Visualize sample data using ee.FeatureCollection.style(**).
styled = samples

def func_uiv (feature):
    return feature.set('style', {
      'pointSize': feature.getNumber('elevation').unitScale(VIS_MIN, VIS_MAX) \
          .multiply(15),
    }) \
  .map(func_uiv) \
  .style(**{
    'color': '000000FF',
    'fillColor': '00000000',
    'styleProperty': 'style',
    'neighborhood': 6,  # increase to correctly draw large points
  })
Map.addLayer(styled)

# Each sample feature has a point geometry and a property named 'elevation'
# corresponding to the band named 'elevation' of the image. If there are
# multiple bands they will become multiple properties. This will print:
#
# geometry: Point (-110.01, 40.00)
# properties:
#   elevation: 1639
print(samples.first())

Best Answer

It's a javascript v.s. python argument passing issue. You're passing a single positional argument (a dict) to image.sample instead of the required positional or keyword arguments.

So image.sample is trying to use the entire dict (below) as the region argument and that's not a valid GeoJSON/ee.Geometry.Polygon object:

{
  'region': region,
  'geometries': True,
}

Instead, you can use keyword args

samples = image.sample(region=region, etc...)

Or dict unpacking (**kwargs) to create keyword args from the dict

samples = image.sample(**{
  'region': region, 
  etc...
})