0

I am trying to clip an image over a FeatureCollection and then export each cropped image using the Python API.

import ee
# Authenticate to Earth Engine
ee.Initialize()

# Load the images and feature collections
age = ee.Image('users/celsohlsj/public/secondary_vegetation_age_collection71_v5').select('classification_2020')
biomass = ee.Image('projects/ee-ana-zonia/assets/biomass_2020')
sd = ee.Image('projects/ee-ana-zonia/assets/biomass_sd_2020')
cwd = ee.Image('projects/ee-ana-zonia/assets/cwd_chave')

# Regions of interest
amazon_biome = ee.FeatureCollection('projects/ee-ana-zonia/assets/amazon_biome_border')
indig_land = ee.FeatureCollection('projects/ee-ana-zonia/assets/indig_land')
ecoregions = ee.FeatureCollection("RESOLVE/ECOREGIONS/2017")
ecoregions = ecoregions.filterBounds(amazon_biome.geometry())
ecoregions_list = ecoregions.toList(ecoregions.size())

# Clip to Amazon biome
age = age.clip(amazon_biome).updateMask(age.gt(0))
biomass = biomass.clip(amazon_biome)
sd = sd.clip(amazon_biome)
cwd = cwd.clip(amazon_biome)

# add and rename bands
img_export = age.addBands(biomass).addBands(sd).addBands(cwd).rename(['age', 'agbd', 'agbd_sd', 'cwd'])
# Reproject to 10m
img_export = img_export.reproject(crs=age.projection(), scale=10)
# Reaggregate to 30m (mean value)
img_export = img_export.reduceResolution(reducer=ee.Reducer.mean()).reproject(crs=age.projection())
# Mask only to regions with age greater than zero (secondary forests)
img_export = img_export.updateMask(age).float()

def export_by_ecoregion(feature):
    ecoreg = ee.Feature(feature)
    img = img_export.clip(ecoreg)
    task = ee.batch.Export.image.toDrive(
        image=img,
        description=f"img_export_{ecoreg.get('ECO_ID').getInfo()}",
        folder='drive_export',
        region=ecoreg.geometry(),
        crs=img.projection().getInfo()['crs'],
        crsTransform=img.projection().getInfo()['transform'],
        skipEmptyTiles = True,
        maxPixels=4e10
    )
    task.start()

ecoregions.map(export_by_ecoregion)

The code exports perfectly when I run then one by one, but if I try to use .map() I get this:

ee.ee_exception.EEException: A mapped function's arguments cannot be used in client-side operations

I understand .getInfo is client side, is there a way I can get the same output on the server side without running the code by hand?

I also tried a for loop to iterate over a list of features made from the FeatureCollection, but that had its own issues (Unable to export unbounded image. (Error code: 3), even if "region" had been specified correctly). I had read that for loops are discouraged in GEE, I believe that may be why.

1 Answer 1

2

Assuming the ECO_ID property is unique for each feature, then you can do something like this:

https://code.earthengine.google.com/59f7ea67318cbc51e782bb6ef3bb2198

def export_by_ecoregion(eco_id):
    ecoreg =  ecoregions.filter(ee.Filter.eq("ECO_ID",eco_id))
    img = img_export.clip(ecoreg)
    proj = img.projection().getInfo()
    task = ee.batch.Export.image.toDrive(
            image=img,
            description=f"img_export_{eco_id}",
            folder='drive_export',
            region=ecoreg.geometry(),
            crs=proj["crs"],
            crsTransform=proj["transform"],
            skipEmptyTiles = True,
            maxPixels=4e10
        )
    task.start()

for eco_id in ecoregions.aggregate_array("ECO_ID").getInfo():
    export_by_ecoregion(eco_id)

However, I would double check what you are trying to do with the reprojections, it might not be what you are expecting. Here's the projection information that you are getting:

{
  "type": "Projection",
  "crs": "EPSG:4326",
  "transform": [
    0.0002694945852358564,
    0,
    -73.99027736567487,
    0,
    -0.0002694945852358564,
    5.271044592628117
  ]
}

Recommended reading: https://developers.google.com/earth-engine/guides/projections#the-default-projection

Since it's the same value for each image, you can remove the call to img.projection().getInfo() and simply hard-code the crs and crsTransform that you want the exports to have.

3
  • 1
    Yeah, Oliver's solution is "the way". Run getInfo on "something" that you can later use as an ideifntier and loop over those. (This is the one place in EE where you actually need to use a for loop). See also: gorelick.medium.com/fast-er-downloads-a2abd512aa26 Nov 28, 2023 at 17:42
  • Awesome, I would've never guessed, thanks a lot! Nov 29, 2023 at 0:52
  • With the reprojections, I was trying to account for edge pixels. I have the age data at 30m resolution and biomass at 100m, so many "age" pixels were overlapped by multiple biomass values. I was advised to reduce the resolution to 10m, aggregate with a mean, and I'd end up with "intermediate" biomass for the edges, which turned out great as I visualized every step of the way. The weird projection is how it actually looks like for the 30m resolution "age" file. I think in this case I couldn't avoid it, but it's good to know that for the future Nov 29, 2023 at 1:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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