davemlz / eemont

A python package that extends Google Earth Engine.
https://eemont.readthedocs.io/
MIT License
407 stars 69 forks source link

Extended methods for the ee.Image class cannot be thrown to map #65

Closed FeiYao-Edinburgh closed 2 years ago

FeiYao-Edinburgh commented 2 years ago

Hi @davemlz ! I recently want to combine part of the extended methods for the ee.Image class into my personal image processing workflow. However, I found that those methods cannot be thrown into map. As a simple illustration, the following code excerpt will prompts the error of A mapped function's arguments cannot be used in client-side operations.

import ee, eemont
ee.Initialize()

def user_process(image):
    return image.scaleAndOffset()

image_coll = ee.ImageCollection("COPERNICUS/S2_SR").limit(3).map(user_process)

I definitely understand that the above stupid example can be simply resolved by:

image_coll = ee.ImageCollection("COPERNICUS/S2_SR").limit(3).scaleAndOffset()

However, won't you think it would be great if extended methods for the ee.Image class can be used as identically as standard methods for the ee.Image class? For instance, the following another stupid example would work perfectly:

import ee, eemont
ee.Initialize()

def user_process(image):
    return image.add(1).subtract(1)

image_coll = ee.ImageCollection("COPERNICUS/S2_SR").limit(3).map(user_process)

I also checked that eemont is powered by eeExtra and throwing to ee_extra.STAC.core.scaleAndOffset(image) to map would cause the same issue. As such, I am not sure if it would be too complicated to have this feature which although may be helpful at some cases.

All the best, Fei

davemlz commented 2 years ago

Hi @FeiYao-Edinburgh!

I understand what you're trying to do. I ran into this problem a long time ago (https://github.com/davemlz/eemont/issues/11), but unfortunately there is no actual way to solve it since I have to maintain the core STAC function on the client-side: this in order to keep all datasets updated autimatically.

FeiYao-Edinburgh commented 2 years ago

Hi @davemlz !

Thanks for your quick reply. I now see the root of evil is the use of getInfo() which is not allowed in map. In this sense, I will choose to use extended methods of eemont simply before and outside my personal user_process:

def user_process(image):
    # Do some stuff
    return image

# For a single image:
image = image.maskClouds()
image = image.scaleAndOffset()
image = user_process(image)

# For an image collection:
image_coll = image_coll.maskClouds()
image_coll = image_coll.scaleAndOffset()
image_coll = image_coll.map(user_process)

Not ideal but still doable. Hopefully all can be thrown into the user_process() function in the future with the improvement of earthengine-api capability.

Slightly deviated from but similar to this topic, it is weird that ee.Image() does not accept ee.String() as input, e.g..

All the best, Fei

FeiYao-Edinburgh commented 2 years ago

Alternatively:

def user_process_coll(image):
    # Do some stuff
    return image

def user_process(image):
    image = image.maskClouds()
    image = image.scaleAndOffset()
    image = user_process_coll(image)
    return image

# For a single image:
image = user_process(image)

# For an image collection:
image_coll = image_coll.maskClouds()
image_coll = image_coll.scaleAndOffset()
image_coll = image_coll.map(user_process_coll)
davemlz commented 2 years ago

Yes!

Also, you can go:

image_col.preprocess().map(user_process_coll)

The preprocess() method calls all available pre-processing steps, including maskClouds() and scaleAndOffset()