Google Earth Engine – Calculate Categorical Image Area Based on Frequency Histogram

google-earth-enginegoogle-earth-engine-javascript-api

To calculate the area of the classes in a categorical image I'm using a ee.Image.reduceRegion.

Sources:


var geometry = 
    ee.Geometry.Polygon(
        [[[12.124393381085076, 42.35185853899329],
          [12.124393381085076, 42.28788783747578],
          [12.241123117413201, 42.28788783747578],
          [12.241123117413201, 42.35185853899329]]], null, false);

var lc = ee.ImageCollection('COPERNICUS/Landcover/100m/Proba-V-C3/Global')
  .filter(ee.Filter.date("2015-01-01", "2015-12-31"))
  .select("discrete_classification")
  .first()

var scale = lc.projection().nominalScale()

And applying the reduction:


var area = ee.Image.pixelArea().divide(1e4)
  .addBands(lc)
  .reduceRegion({
    reducer:ee.Reducer.sum().group(1), 
    geometry:geometry,
    scale:scale
  }
)

// Reducing all the classes to get a single comparable value
var reduced_area = ee.Number(ee.List(area.get("groups")).map(function(group){
  return ee.Dictionary(group).get("sum")
}).reduce(ee.Reducer.sum()))

print("reduced_count", reduced_count)

# Outputs:
# "pixel_area"
# 6837.526895473032

However, I'm forced to use an API that is reducing the region with ee.Reducer.frequencyHistogram, I assume that I could calculate the area from each category just by multiplying each pixel count per class by the pixel area, but the results are not even close when I compare with the previous result.


// Not grouping to get an overall value to compare with previous calcualtion.
var frequency = lc
  .reduceRegion({
    reducer:ee.Reducer.frequencyHistogram(), 
    geometry:geometry,
    scale:scale
  }
)

print(
  'Frequency area', 
  ee.Number(ee.Dictionary(
    frequency.get("discrete_classification")
  ).values()
  .reduce(ee.Reducer.sum()))
  .multiply(scale.pow(2)).divide(1e4) // dumb, but just for clarity
)

# Outputs:
# "Frequency area"
# 12514.60784313726

So, why is this difference? can I calculate the area based on the frequency histogram?

Best Answer

You're not specifying a projection anywhere in your code, so

  1. You don't actually know what projection you're using in the reduceRegion calls (probably EPSG:3857 because that's the projection of your lc image), and

  2. Whatever you're using, it doesn't have equal area pixels, so you get different area per pixel at different latitudes.

At the latitude you're working at, the pixels are approximately 5457 sqm in the original EPSG:3857 projection of the image, not the 10,000 sqm you were expecting (so you're off by about 50%).

You can switch to an equal area projection valid in your region of interest, and the multiplication method will work out to be almost identical.

var frequency = lc
  .reduceRegion({
    reducer:ee.Reducer.frequencyHistogram(), 
    geometry:geometry,
    crs: ee.Projection("EPSG:3035").atScale(100)
  }
)

# Frequency area 6837.443137254904

https://code.earthengine.google.com/a9dfe316e48a6bad8136cfd3043aa028