TransWikia.com

Nested functions in EE python API

Geographic Information Systems Asked on December 22, 2020

I am trying to divide a feature collection into a mosaic of different cells in order to aid with exporting over a large area. I found the following script which is mentioned in this stack overflow question to divide a geometry into bite-sized areas and I am trying to convert it to Python so that I can export data in an iterative fashion.

I have considered this question and this question, but neither seem to adequately capture the complexity of the above script. The issue pertains to how python treats scope and I have tried doing a (rather dumb) method of carrying variables as arguments through each and every function, but this breaks down when we are forced to use .map()

Here is my naive approach to translating between the two (mostly converted using geemap:: js_snippet_to_py() function.

def tile(region, size_in_degrees):
    def tile_geometry(geometry):
        coords = ee.List(geometry.bounds().coordinates().get(0))
        min_point = ee.List(coords.get(0))
        max_point = ee.List(coords.get(2))

        def sequence(direction):
            start = min_point.get(direction)
            stop = ee.Number(max_point.get(direction))
            return ee.List.sequence(start, stop, size_in_degrees)

        def build_numbers(y):
            return ee.Feature(ee.Geometry.Rectangle([[ee.Number(x), ee.Number(y)], 
                [ee.Number(x).add(size_in_degrees).min(max_point.get(0)), 
                  ee.Number(y).add(size_in_degrees).min(max_point.get(1)) 
                ]]).intersection(geometry, 30))
        
        def func_nyp(x):
            return sequence(1).map(build_numbers)
        
        tiles = sequence(0).map(func_nyp).flatten()

    return ee.FeatureCollection(tiles).filterBounds(geometry)

    def tile_feature_collection(feature_collection):
        return ee.FeatureCollection(
          feature_collection.iterate(iterate_func))
    
    def iterate_func(feature, acc):
        return ee.FeatureCollection(acc).merge(tile_geometry(ee.Feature(feature).geometry())), ee.FeatureCollection([])


     if (isinstance(region, ee.FeatureCollection)):
        return tile_feature_collection(region)
    elif (isinstance(region, ee.Feature)):
        return tile_geometry(region.geometry())
    elif (isinstance(region,ee.Geometry)):
        return tile_geometry(region)
    else:
        print('region must be ee.FeatureCollection, ee.Feature, or ee.Geometry. Was ' + str(region))

When I run the above code, the sequence() function is not known to the global environment. However, when I try to extract each of the nested functions, I quickly run into issues surrounding variables that need to be carried through from others.

One Answer

I haven't tried running the code, but here are the translation mistakes I see.


return ee.FeatureCollection(sequence(0).map(func_nyp).flatten()).filterBounds(geometry)

should be indented one more level so that it is part of tile_geometry (in which sequence is defined) instead of part of tile (where sequence is out of scope).

It also could be simplified by using the variable you defined,

return ee.FeatureCollection(tiles).filterBounds(geometry)

but that won't change the behavior.


def build_numbers(y):
    return ee.Feature(ee.Geometry.Rectangle([[ee.Number(x), ee.Number(y)], 
        [ee.Number(x).add(size_in_degrees).min(max_point.get(0)), 
          ee.Number(y).add(size_in_degrees).min(max_point.get(1)) 
        ]]).intersection(geometry, 30))

def func_nyp(x):
    return sequence(1).map(build_numbers)

Here build_numbers uses x, so it needs to be defined where x is:

def func_nyp(x):
    def build_numbers(y):
        return ee.Feature(ee.Geometry.Rectangle([[ee.Number(x), ee.Number(y)],
            [ee.Number(x).add(size_in_degrees).min(max_point.get(0)),
              ee.Number(y).add(size_in_degrees).min(max_point.get(1))
            ]]).intersection(geometry, 30))

    return sequence(1).map(build_numbers)

I also removed the backslashes — they are unnecessary in Python whenever there are parentheses or brackets around the expression.

General advice: don't assume you can make functions less nested than they were, unless you've examined them to ensure they don't use any outer variables.

Also remember that you can use Python lambda expressions instead of named functions to write functions in-line as in JavaScript, though they can be harder to read.

Correct answer by Kevin Reid on December 22, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP