def testCallAndApply(self):
    """Verifies library initialization."""

    # Use a custom set of known functions.
    def MockSend(path, params, unused_method=None, unused_raw=None):
      if path == '/algorithms':
        return {
            'fakeFunction': {
                'type': 'Algorithm',
                'args': [
                    {'name': 'image1', 'type': 'Image'},
                    {'name': 'image2', 'type': 'Image'}
                ],
                'returns': 'Image'
            },
            'Image.constant': apitestcase.BUILTIN_FUNCTIONS['Image.constant']
        }
      else:
        raise Exception('Unexpected API call to %s with %s' % (path, params))
    ee.data.send_ = MockSend

    ee.Initialize()
    image1 = ee.Image(1)
    image2 = ee.Image(2)
    expected = ee.Image(ee.ComputedObject(
        ee.ApiFunction.lookup('fakeFunction'),
        {'image1': image1, 'image2': image2}))

    applied_with_images = ee.apply(
        'fakeFunction', {'image1': image1, 'image2': image2})
    self.assertEquals(expected, applied_with_images)

    applied_with_numbers = ee.apply('fakeFunction', {'image1': 1, 'image2': 2})
    self.assertEquals(expected, applied_with_numbers)

    called_with_numbers = ee.call('fakeFunction', 1, 2)
    self.assertEquals(expected, called_with_numbers)

    # Test call and apply() with a custom function.
    sig = {'returns': 'Image', 'args': [{'name': 'foo', 'type': 'Image'}]}
    func = ee.CustomFunction(sig, lambda foo: ee.call('fakeFunction', 42, foo))
    expected_custom_function_call = ee.Image(
        ee.ComputedObject(func, {'foo': ee.Image(13)}))
    self.assertEquals(expected_custom_function_call, ee.call(func, 13))
    self.assertEquals(expected_custom_function_call,
                      ee.apply(func, {'foo': 13}))

    # Test None promotion.
    called_with_null = ee.call('fakeFunction', None, 1)
    self.assertEquals(None, called_with_null.args['image1'])
def _get_area_histogram(image, polygons, classes):
    """Computes the area of class in each polygon.

    Args:
      image: The single-band image with class-valued pixels.
      polygons: An ee.FeatureCollection of polygons to analyse.
      classes: The integer class values to compute area for.

    Returns:
      A list of dictionaries, one for each polygon in the polygons table in
      the same order as they are returned from the collection. Each dictionary
      includes a "name" property from the original polygon row, a "total"
      property with the total classified polygon area in square meters and an
      entry for each class, the key being the class value and the value being
      the area of that class in square meters.
    """
    stats_image = ee.call(
        'SAD/com.google.earthengine.examples.sad.GetStats',
        image, polygons, 'name')
    stats = ee.data.getValue({
        'image': stats_image.serialize(False),
        'fields': 'classHistogram'
    })['properties']['classHistogram']['values']

    result = []
    for name, value in stats.iteritems():
        # The values are sorted to get a deterministic order, as this affects
        # the result due to floating point errors.
        row = {'name': name, 'total': sum(sorted(value['values'].values()))}
        for class_number in classes:
          class_label = str(class_number)
          row[class_label] = value['values'].get(class_label, 0)
        result.append(row)

    return result
Example #3
0
 def common_area_func(refl_toa):
     #### Common area
     common_area = refl_toa.mask().reduce(ee.call("Reducer.and"))
     refl_toa = refl_toa.mask(common_area)
     #### Cloud mask
     cloud_mask = ee.Algorithms.Landsat.simpleCloudScore(refl_toa) \
         .select(['cloud']).lt(ee.Image.constant(50))
     return refl_toa.mask(cloud_mask.mask(cloud_mask))
  def testDatePromtion(self):
    # Make a feature, put a time in it, and get it out as a date.
    self.InitializeApi()
    point = ee.Geometry.Point(1, 2)
    feature = ee.Feature(point, {'x': 1, 'y': 2})
    date_range = ee.call('DateRange', feature.get('x'), feature.get('y'))

    # Check that the start and end args are wrapped in a call to Date.
    self.assertEquals(date_range.args['start'].func._signature['name'], 'Date')
    self.assertEquals(date_range.args['end'].func._signature['name'], 'Date')
def _get_area_histogram(image, polygons, classes, scale=120):
    """Computes the area of class in each polygon.

    Args:
      image: The single-band image with class-valued pixels.
      polygons: An ee.FeatureCollection of polygons to analyse.
      classes: The integer class values to compute area for.

    Returns:
      A list of dictionaries, one for each polygon in the polygons table in
      the same order as they are returned from the collection. Each dictionary
      includes a "name" property from the original polygon row, a "total"
      property with the total polygon area in square meters and an entry for
      each class, the key being the class value and the value being the area
      of that class in square meters.
    """
    if ASSUME_NEW_API:
        area = ee.Image.pixelArea()
        sum_reducer = ee.call('Reducer.sum')

        def calculateArea(feature):
            geometry = feature.geometry()
            total = area.mask(image.mask())
            total_area = total.reduceRegion(
                sum_reducer, geometry, scale, bestEffort=True)
            properties = {'total': total_area}

            for class_value in classes:
                masked = area.mask(image.eq(class_value))
                class_area = masked.reduceRegion(
                    sum_reducer, geometry, scale, bestEffort=True)
                properties[str(class_value)] = class_area

            return ee.call('SetProperties', feature, properties)

        result = polygons.map(calculateArea).getInfo()
        return [i['properties'] for i in result['features']]
    else:
        stats_image = ee.Image({
            'creator': 'SAD/com.google.earthengine.examples.sad.GetStats',
            'args': [image, polygons, 'name']
        })
        stats = ee.data.getValue({
            'image': stats_image.serialize(False),
            'fields': 'classHistogram'
        })['properties']['classHistogram']['values']

        result = []
        for name, value in stats.iteritems():
            row = {'name': name}
            for cls in classes:
              row[cls] = value['values'].get(str(cls), 0)
            result.append(row)

        return result
def tcTransform(image):
	b = ee.Image(image).select(["B1", "B2", "B3", "B4", "B5", "B7"])
	brt_coeffs = ee.Image([0.2043, 0.4158, 0.5524, 0.5741, 0.3124, 0.2303])
	grn_coeffs = ee.Image([-0.1603, -0.2819, -0.4934, 0.7940, -0.0002, -0.1446])
	wet_coeffs = ee.Image([0.0315, 0.2021, 0.3102, 0.1594, -0.6806, -0.6109])

	sum = ee.call("Reducer.sum")
	brightness = b.multiply(brt_coeffs).reduce(sum)
	greenness = b.multiply(grn_coeffs).reduce(sum)
	wetness = b.multiply(wet_coeffs).reduce(sum)

	return ee.Image(brightness).addBands(greenness).addBands(wetness).select([0,1,2], ['B','G','W'])
def _get_landsat_toa(start_time, end_time, version=-1):
    """Returns a Landsat 7 TOA ee.ImageCollection for a given time period.

    Args:
      start: The start of the time period as a Unix timestamp.
      end: The end of the time period as a Unix timestamp.
    """
    collection = ee.ImageCollection({
        'id': 'L7_L1T',
        'version': version
    })
    collection = collection.filterDate(start_time, end_time)
    return collection.map(lambda img: ee.call('LandsatTOA', img))
        def calculateArea(feature):
            geometry = feature.geometry()
            total = area.mask(image.mask())
            total_area = total.reduceRegion(
                sum_reducer, geometry, scale, bestEffort=True)
            properties = {'total': total_area}

            for class_value in classes:
                masked = area.mask(image.eq(class_value))
                class_area = masked.reduceRegion(
                    sum_reducer, geometry, scale, bestEffort=True)
                properties[str(class_value)] = class_area

            return ee.call('SetProperties', feature, properties)
Example #9
0
def __learning_threshold(domain, algorithm):
    
    training_domain = None
    if domain.training_domain:
        training_domain = domain.training_domain
    elif domain.unflooded_domain:
        training_domain = domain.unflooded_domain
    if not training_domain:
        raise Exception('Cannot use learning algorithms without a training image defined by the domain!')
    classifier = ee.apply('TrainClassifier', {'image': training_domain.get_radar().image,
                            'subsampling'       : 0.07,
                            'training_image'    : training_domain.ground_truth,
                            'training_band'     : 'b1',
                            'training_region'   : training_domain.bounds,
                            'max_classification': 2,
                            'classifier_name'   : algorithm})
    classified = ee.call('ClassifyImage', domain.get_radar().image, classifier).select(['classification'], ['b1']);
    return classified;
  def testUnboundMethods(self):
    """Verifies unbound method attachment to ee.Algorithms."""

    # Use a custom set of known functions.
    def MockSend(path, unused_params, unused_method=None, unused_raw=None):
      if path == '/algorithms':
        return {
            'Foo': {
                'type': 'Algorithm',
                'args': [],
                'description': '',
                'returns': 'Object'
            },
            'Foo.bar': {
                'type': 'Algorithm',
                'args': [],
                'description': '',
                'returns': 'Object'
            },
            'Quux.baz': {
                'type': 'Algorithm',
                'args': [],
                'description': '',
                'returns': 'Object'
            },
            'last': {
                'type': 'Algorithm',
                'args': [],
                'description': '',
                'returns': 'Object'
            }
        }
    ee.data.send_ = MockSend

    ee.ApiFunction.importApi(lambda: None, 'Quux', 'Quux')
    ee._InitializeUnboundMethods()

    self.assertTrue(callable(ee.Algorithms.Foo))
    self.assertTrue(callable(ee.Algorithms.Foo.bar))
    self.assertTrue('Quux' not in ee.Algorithms)
    self.assertEquals(ee.call('Foo.bar'), ee.Algorithms.Foo.bar())
    self.assertNotEquals(ee.Algorithms.Foo.bar(), ee.Algorithms.last())
Example #11
0
	def make_mosaic( self, opt):
		EarthEngine( current_handler )
		collection = ee.ImageCollection(opt['sat'][1])
		vcf = ee.Image('MOD44B_C4_TREE_2000')
		elevation = ee.Image('srtm90_v4')
		sensor = opt['sat'][0]
		starttime = opt['times'][0]['starttime']
		endtime = opt['times'][0]['endtime']
		#polygon = [ polyBbox( opt['bbox'] ) ]
		polygon = ee.Geometry.Rectangle(-69.9365234375,-11.6928965625,-69.8161621094,-11.4097965708)
		call = CLASLITE + 'MosaicScene'
		mosaic = ee.call(call,
			collection, 
			vcf, 
			elevation,
			sensor,
			starttime,
			endtime, 
			polygon)
		return mosaic
Example #12
0
def GetTrendyMapId():
  global cloudiness

  """Returns the MapID for the night-time lights trend map."""

  startdate="2012-01-01"
  stopdate="2012-12-31"
  collection = ee.ImageCollection("MOD09GA").filterDate(ee.Date(startdate), stopdate)

  collection = collection.map(lambda img: img.select('state_1km').expression("((b(0))%4)==1|((b(0))%4)==2"))

  MEAN = ee.call('Reducer.mean')
  c100 = ee.Image(100)

  cloudiness = collection.reduce(MEAN).multiply(c100)

  # Fit a linear trend to the nighttime lights collection.
  palette="000000,00FF00,FF0000"

  return cloudiness.getMapId({'min':0,'max':100,'palette':palette})
  def testDynamicClasses(self):
    """Verifies dynamic class initialization."""

    # Use a custom set of known functions.
    def MockSend(path, unused_params, unused_method=None, unused_raw=None):
      if path == '/algorithms':
        return {
            'Array': {
                'type': 'Algorithm',
                'args': [
                    {
                        'name': 'values',
                        'type': 'Serializable',
                        'description': ''
                    }
                ],
                'description': '',
                'returns': 'Array'
            },
            'Array.cos': {
                'type': 'Algorithm',
                'args': [
                    {
                        'type': 'Array',
                        'description': '',
                        'name': 'input'
                    }
                ],
                'description': '',
                'returns': 'Array'
            },
            'Kernel.circle': {
                'returns': 'Kernel',
                'args': [
                    {
                        'type': 'float',
                        'description': '',
                        'name': 'radius',
                    },
                    {
                        'default': 1.0,
                        'type': 'float',
                        'optional': True,
                        'description': '',
                        'name': 'scale'
                    },
                    {
                        'default': True,
                        'type': 'boolean',
                        'optional': True,
                        'description': '',
                        'name': 'normalize'
                        }
                    ],
                'type': 'Algorithm',
                'description': ''
            },
            'Reducer.mean': {
                'returns': 'Reducer',
                'args': []
            },
            'fakeFunction': {
                'returns': 'Array',
                'args': [
                    {
                        'type': 'Reducer',
                        'description': '',
                        'name': 'kernel',
                    }
                ]
            }
        }
    ee.data.send_ = MockSend

    ee.Initialize()

    # Verify that the expected classes got generated.
    self.assertTrue(hasattr(ee, 'Array'))
    self.assertTrue(hasattr(ee, 'Kernel'))
    self.assertTrue(hasattr(ee.Array, 'cos'))
    self.assertTrue(hasattr(ee.Kernel, 'circle'))

    # Try out the constructors.
    kernel = ee.ApiFunction('Kernel.circle').call(1, 2)
    self.assertEquals(kernel, ee.Kernel.circle(1, 2))

    array = ee.ApiFunction('Array').call([1, 2])
    self.assertEquals(array, ee.Array([1, 2]))
    self.assertEquals(array, ee.Array(ee.Array([1, 2])))

    # Try out the member function.
    self.assertEquals(ee.ApiFunction('Array.cos').call(array),
                      ee.Array([1, 2]).cos())

    # Test argument promotion.
    f1 = ee.ApiFunction('Array.cos').call([1, 2])
    f2 = ee.ApiFunction('Array.cos').call(ee.Array([1, 2]))
    self.assertEquals(f1, f2)
    self.assertTrue(isinstance(f1, ee.Array))

    f3 = ee.call('fakeFunction', 'mean')
    f4 = ee.call('fakeFunction', ee.Reducer.mean())
    self.assertEquals(f3, f4)

    try:
      ee.call('fakeFunction', 'moo')
      self.fail()
    except ee.EEException as e:
      self.assertTrue('Unknown algorithm: Reducer.moo' in str(e))
Example #14
0
    def get(self):

        ee.Initialize(config.EE_CREDENTIALS, config.EE_URL)
        
        # Gather http req params
        sciname = self.request.get('sciname', None)
        habitats = self.request.get('habitats', None)
        elevation = self.request.get('elevation', None)
        year = self.request.get('year', None)
        get_area = self.request.get('get_area', 'false')
        ee_id = self.request.get('ee_id', None)
        minx = self.request.get('minx', -179.99)
        miny = self.request.get('miny', -89.99)
        maxx = self.request.get('maxx', 179.99)
        maxy = self.request.get('maxy', 89.99)

        # Get images for the year of interest
        cover = ee.Image(mol_assets.modis51[year])
        elev = ee.Image(mol_assets.elevation)        
        pop = ee.Image(mol_assets.population[year])
        pop = pop.mask(pop.lt(0))
        # Get the species range map
        species = ee.Image(ee_id)

        # Elevation range and habitats for this species 
        min = int(elevation.split(',')[0])
        max = int(elevation.split(',')[1])
        habitat_list = habitats.split(",")


        output = ee.Image(0).mask(species)

        for pref in habitat_list:
            if pref == 17:
                mod51pref = 0
            else:
                mod51pref = pref
            
            output = output.where(
                cover.eq(int(mod51pref)).And(elev.gt(min)).And(elev.lt(max)),
                int(mod51pref)
            )

        result = output.mask(output)

        if(get_area == 'false'):
            
            # just return Map ID's
            map = result.getMapId({
                'palette': 'aec3d4,152106,225129,369b47,30eb5b,387242,6a2325,'\
                        'c3aa69,b76031,d9903d,91af40,111149,cdb33b,cc0013,'\
                        '33280d,d7cdcc,f7e084,6f6f6f',
                'min':0,
                'max': 17,
                'opacity': 1
            })
            getTileUrl = 'https://earthengine.googleapis.com/'\
                'map/%s/{Z}/{X}/{Y}?token=%s' % (
                    map['mapid'],
                    map['token']
                )


            self.response.out.write(json.dumps(getTileUrl))
        else:
            # compute the area and population
            area = ee.call("Image.pixelArea")
            sum_reducer = ee.call("Reducer.sum")
            
            area = area.mask(species)
            total = area.mask(result.mask())
          
            pop = pop.mask(species)
            pop = pop.where(pop.lt(0),0)
            
           # Generate a region to do the calculation over
            region = ee.Feature(
                ee.Feature.Polygon(
                    [[float(minx), float(miny)],
                     [float(minx), float(maxy)],
                     [float(maxx), float(maxy)],
                     [float(maxx), float(miny)],
                     [float(minx), float(miny)]
                     ]))
            geometry = region.geometry()

            # #compute area on 1km scale
            clipped_area = total.reduceRegion(
                sum_reducer, geometry, maxPixels=10000000000)
            total_pop = pop.reduceRegion(
                sum_reducer, geometry, maxPixels=10000000000)
            
            properties = {
                'clipped_area': clipped_area,
                'total_pop': total_pop,
            }

            region = region.set(properties)

            data = ee.data.getValue({"json": region.serialize()})
            data = data["properties"]
            species_stats = {
               'clipped_area': round(
                    (data["clipped_area"]["area"]) / 1000000, 3),
               'total_pop': round(
                    data["total_pop"]["b1"], 0)
            }

            self.response.out.write(
                json.dumps(species_stats)
            )
Example #15
0
    def get(self):

        ee.Initialize(config.EE_CREDENTIALS, config.EE_URL)

        sciname = self.request.get('sciname', None)
        habitats = self.request.get('habitats', None)
        elevation = self.request.get('elevation', None)
        year = self.request.get('year', None)
        mode = self.request.get('mode', 'area')
        ee_id = self.request.get('ee_id', None)
        minlng = self.request.get('minx', None)
        maxlng = self.request.get('maxx', None)
        minlat = self.request.get('miny', None)
        maxlat = self.request.get('maxy', None)
        
        
        #Get land cover and elevation layers
        cover = ee.Image('MCD12Q1/MCD12Q1_005_%s_01_01' % 
                         (year)).select('Land_Cover_Type_1')
                         
        elev = ee.Image('GME/images/04040405428907908306-08319720230328335274')
        #elev = ee.Image('srtm90_v4')
        #minlat = round(extent["sw"]["lat"]*1000)/1000
        
        #minlng = round(extent["sw"]["lng"]*1000)/1000
        
        #maxlat = round(extent["ne"]["lat"]*1000)/1000
        
        #maxlng = round(extent["ne"]["lng"]*1000)/1000
    
        #define a bounding polygon for the reducers
       
        region = ee.Feature(
            ee.Feature.Polygon([
                [float(minlng),float(minlat)],
                [float(minlng),float(maxlat)],
                [float(maxlng),float(maxlat)],
                [float(maxlng),float(minlat)],
                [float(minlng),float(minlat)]
            ])
        )
        geometry = region.geometry()
        
       
        output = ee.Image(0)
        empty = ee.Image(0).mask(0)

        species = ee.Image(ee_id)
        
        
        
        #parse the CDB response

        min = int(elevation.split(',')[0])
        max = int(elevation.split(',')[1])
        habitat_list = habitats.split(",")

        output = output.mask(species)

        for pref in habitat_list:
            for year in range(2001,2010):
                cover = ee.Image('MCD12Q1/MCD12Q1_005_%s_01_01' % 
                             (year)).select('Land_Cover_Type_1')
                output = output.where(
                    cover.eq(int(pref)).And(elev.gt(min)).And(elev.lt(max)),1)

        result = output.mask(output)

        if mode == 'range':
            
            rangeMap = result.getMapId({
                'palette': '000000,85AD5A',
                'max': 1,
                'opacity': 0.8
            })
            response = {
                'maps' : [
                    EE_TILE_URL % 
                         (rangeMap['mapid'], rangeMap['token'])
                    
                ]
            }
            self.response.headers["Content-Type"] = "application/json"
            self.response.out.write(json.dumps(response))
        elif mode == 'assess':
            pointjson = self.getRandomPoints(sciname)
            pjson = json.loads(pointjson)
            
            logging.info(json.dumps(pjson))
            
            if pjson["total_rows"] == 0:
               self.response.headers["Content-Type"] = "application/json"
               self.response.out.write(json.dumps({
                                        "has_pts" : False
                                        }
                                       ))    
            else:
                #Create an array of Points
                pts = []
                for row in pjson["rows"]:
                    pts.append(
                       ee.Feature(
                          ee.Feature.Point(
                             row["lon"],row["lat"]),
                             {'val':0 }))
                
                #Create a FeatureCollection from that array 
                pts_fc = ee.FeatureCollection(pts)
                logging.info('Created the point FC')
                #this code reduces the point collection to an image.  Each pixel 
                #contains a count for the number of pixels that intersect with it
                #then, the point count image is masked by the range
                #imgPoints = pts_fc.reduceToImage(['val'], ee.call('Reducer.sum')).mask(result);
                #imgOutPoints = pts_fc.reduceToImage(['val'], ee.call('Reducer.sum')).mask(result.neq(1));
                #now, just sum up the points image.  this is the number of points that overlap the range
                #ptsIn = imgPoints.reduceRegion(ee.call('Reducer.sum'), geometry, 10000)
                
                #This would be for making pts_in and pts_out FeatureCollections 
                #that can be mapped in different colors. Doesn't work...
                #ptsInFC = imgPoints.reduceToVectors(None,None,100000000)
                #ptsOutFC = imgOutPoints.reduceToVectors(None, None, 100000000)
                
                #data = ee.data.getValue({"json": ptsIn.serialize()})
                #pts_in = data["sum"]
                
                #Sample the range map image
                coll = ee.ImageCollection([result])
                logging.info('Sample the image')
                sample = coll.getRegion(pts_fc,10000).getInfo()
                logging.info('Sampled it')
                #Create a FC for the points that fell inside the range
                pts_in = []
                for row in sample[1:]:
                    pts_in.append(
                        ee.Feature(
                            ee.Feature.Point(row[1], row[2]),{'val': 1})
                    )
                
                pts_in_fc = ee.FeatureCollection(pts_in)
                
                #reverse Join to get the ones that are outside the range
                pts_out_fc = pts_fc.groupedJoin(
                    pts_in,'within_distance',distance=10000,mode='inverted')
                
                pts_out_map = pts_out_fc.getMapId({'color': 'e02070'})
                pts_in_map = pts_in_fc.getMapId({'color': '007733'})
                
                response = {
                    'maps' : [
                        EE_TILE_URL % 
                             (pts_in_map['mapid'],pts_in_map['token']),
                        EE_TILE_URL % 
                             (pts_out_map['mapid'],pts_out_map['token'])
                        
                    ],
                    'has_pts' : True,
                    'pts_in' : len(pts_in),
                    'pts_tot' : len(pts)
                    # add points stats to result
                }
                self.response.headers["Content-Type"] = "application/json"
                self.response.out.write(json.dumps(response))
        else:
        #compute the area
          
            area = ee.call("Image.pixelArea")
            sum_reducer = ee.call("Reducer.sum")
            
            area = area.mask(species)
            total = area.mask(result.mask())

            ##compute area on 10km scale
            clipped_area = total.reduceRegion(
                sum_reducer,geometry,scale=1000,bestEffort=True)
            total_area = area.reduceRegion(
                sum_reducer,geometry,scale=1000,bestEffort=True)

            properties = {'total': total_area, 'clipped': clipped_area}

            region = region.set(properties)

            data = ee.data.getValue({"json": region.serialize()})
            logging.info(json.dumps(data))
            #self.response.headers["Content-Type"] = "application/json"
            #self.response.out.write(json.dumps(data))
            ta = 0
            ca = 0
            ta = data["properties"]["total"]["area"]
            ca = data["properties"]["clipped"]["area"]
            template_values = {
               'clipped_area': ca/1000000,
               'total_area': ta/1000000
            }
            self.response.headers["Content-Type"] = "application/json"
            self.response.out.write(json.dumps(template_values))
uncorrected_thumbnail = uncorrected_image.getThumbUrl({
'bands': 'B4,B3,B2',
'min':7000,
'max':10000,
'size': '800',
'region': maliau_region
})
Image(url=uncorrected_thumbnail)

# <rawcell>

# Get the Digital Terrain Model and the image metadata to help produce the Illumination Condition

# <codecell>

terrain = ee.call('Terrain', ee.Image('srtm90_v4'))
solar_zenith = (90-maliau_image.getInfo()['properties']['SUN_ELEVATION'])
solar_azimuth = maliau_image.getInfo()['properties']['SUN_AZIMUTH']
solar_zenith_radians = (solar_zenith*math.pi)/180
slope_radians = terrain.select(['slope']).expression("(b('slope')*" + str(math.pi) + ")/180")
aspect = terrain.select(['aspect'])

# <rawcell>

# Slope part of the illumination condition

# <codecell>

cosZ = math.cos(solar_zenith_radians)
cosS = slope_radians.cos()
slope_illumination = cosS.expression("b('slope')*(" + str(cosZ) + ")").select(['slope'],['b1'])
Example #17
0
    def testCallAndApply(self):
        """Verifies library initialization."""

        # Use a custom set of known functions.
        def MockSend(path, params, unused_method=None, unused_raw=None):
            if path == '/algorithms':
                return {
                    'fakeFunction': {
                        'type':
                        'Algorithm',
                        'args': [{
                            'name': 'image1',
                            'type': 'Image'
                        }, {
                            'name': 'image2',
                            'type': 'Image'
                        }],
                        'returns':
                        'Image'
                    },
                    'Image.constant':
                    apitestcase.BUILTIN_FUNCTIONS['Image.constant']
                }
            else:
                raise Exception('Unexpected API call to %s with %s' %
                                (path, params))

        ee.data.send_ = MockSend

        ee.Initialize(None)
        image1 = ee.Image(1)
        image2 = ee.Image(2)
        expected = ee.Image(
            ee.ComputedObject(ee.ApiFunction.lookup('fakeFunction'), {
                'image1': image1,
                'image2': image2
            }))

        applied_with_images = ee.apply('fakeFunction', {
            'image1': image1,
            'image2': image2
        })
        self.assertEquals(expected, applied_with_images)

        applied_with_numbers = ee.apply('fakeFunction', {
            'image1': 1,
            'image2': 2
        })
        self.assertEquals(expected, applied_with_numbers)

        called_with_numbers = ee.call('fakeFunction', 1, 2)
        self.assertEquals(expected, called_with_numbers)

        # Test call and apply() with a custom function.
        sig = {'returns': 'Image', 'args': [{'name': 'foo', 'type': 'Image'}]}
        func = ee.CustomFunction(sig,
                                 lambda foo: ee.call('fakeFunction', 42, foo))
        expected_custom_function_call = ee.Image(
            ee.ComputedObject(func, {'foo': ee.Image(13)}))
        self.assertEquals(expected_custom_function_call, ee.call(func, 13))
        self.assertEquals(expected_custom_function_call,
                          ee.apply(func, {'foo': 13}))

        # Test None promotion.
        called_with_null = ee.call('fakeFunction', None, 1)
        self.assertEquals(None, called_with_null.args['image1'])
    def _make_mosaic(self, period, long_span=False):
        """Returns a mosaic of MODIS images for a given period.

        This selects images from the MODIS GA and GQ collections, filtered to
        the specified time range, based on an inclusions table.

        The inclusions table lists the days to include in the mosaic for each
        month, for each MODIS tile. Currently it is a Fusion Table specified
        by MODIS_INCLUSIONS_TABLE, with a row for each (month, modis tile).
        Each row has a geometry of the tile and a comma-separated list of day
        numbers to include in the mosaic.

        Rows that do not have a corresponding image in each collection are
        skipped. If no features overlap an output pixel, we'll fall back on a
        composite constructed from the week of images preceding the period end.

        Args:
          period: The mosaic period, as a dictionary with "start" and "end",
              keys, both containing Unix timestamps.
          long_span: Whether to use an extended period.
              If False, the period is used as is and only the month at the
              midpoint of the period range is used to select from the
              inclusions table.
              If True, the start of the specified period is ignored and a new
              start is computed by extending the end of the period back by
              LONG_SPAN_SIZE_MS milliseconds.

        Returns:
          An ee.Image with the following bands:
          0. num_observations_1km
          1. state_1km
          2. sur_refl_b01_500m
          3. sur_refl_b02_500m
          4. sur_refl_b03_500m
          5. sur_refl_b04_500m
          6. sur_refl_b05_500m
          7. sur_refl_b06_500m
          8. sur_refl_b07_500m
          9. sur_refl_b01_250m
          10. sur_refl_b02_250m
          11. num_observations_250m
        """

        # Calculate the time span.
        if long_span:
          start_time = period['end'] - LONG_SPAN_SIZE_MS
          end_time = period['end']
          start_month = time.gmtime(start_time / 1000).tm_mon
          start_year = time.gmtime(start_time / 1000).tm_year
          end_month = time.gmtime(end_time / 1000).tm_mon
          end_year = time.gmtime(end_time / 1000).tm_year
          start = '%04d%02d' % (start_year, start_month)
          end = '%04d%02d' % (end_year, end_month)
          inclusions_filter = ee.Filter.And(
              ee.Filter.gte('compounddate', start),
              ee.Filter.lte('compounddate', end))
        else:
          start_time = period['start']
          end_time = period['end']
          month = self._getMidMonth(start_time, end_time)
          year = self._getMidYear(start_time, end_time)
          inclusions_filter = ee.Filter.eq(
              'compounddate', '%04d%02d' % (year, month))

        # Prepare source image collections.
        modis_ga = ee.ImageCollection('MODIS/MOD09GA').filterDate(start_time, end_time)
        modis_gq = ee.ImageCollection('MODIS/MOD09GQ').filterDate(start_time, end_time)

        # Prepare the inclusions table.
        inclusions = ee.FeatureCollection(MODIS_INCLUSIONS_TABLE)
        inclusions = inclusions.filter(inclusions_filter)

        return ee.call(
          'SAD/com.google.earthengine.examples.sad.MakeMosaic',
          modis_ga, modis_gq, inclusions, start_time, end_time)
    def ndfi_change_value(self, asset_id, polygon, rows=5, cols=5):
        """Calculates NDFI delta stats between two periods in a given polygon.

        This method splits the supplied polygon using a grid specified by the
        rows and columns parameters, then for each cell, computes the number
        of valid pixels and the total NDFI of all those pixels.

        WARNING: Until ASSUME_NEW_API is flipped, this is horribly, painfully
        slow, and slows down linearly with the number of cells.

        Args:
          asset_id: The string ID of the baseline classification image. Should
              have one band whose values are the CLS_* constants defined in
              this file.
          polygon: The GeoJSON polygon to analyse.
          rows: The number of rows to divide the polygon into.
          columns: The number of columns to divide the polygon into.

        Returns:
          The counts and sums of NDFI pixels for each cell in row-major order.
          For backward-compatibility, returned in the awkward old EE format.
          Example:
          {
            "properties": {
              "ndfiSum": {
                "type": "DataDictionary",
                "values": {
                  "count": [0, 0, 42, 1, 30, 12],
                  "sum": [0, 0, 925, 3, 879, 170]
                }
              }
            }
          }
        """

        # Get region bound.
        bbox = self._get_polygon_bbox(polygon)
        rect = ee.Feature.Rectangle(*bbox)
        min_x = bbox[0]
        min_y = bbox[1]
        max_x = bbox[2]
        max_y = bbox[3]
        x_span = max_x - min_x
        y_span = max_y - min_y
        x_step = x_span / cols
        y_step = y_span / rows

        # Make a numbered grid image that will be used as a mask when selecting
        # cells. We can't clip to geometry because that way some pixels may be
        # included in multiple cells.
        lngLat = ee.Image.pixelLonLat().clip(rect)
        lng = lngLat.select(0)
        lat = lngLat.select(1)

        lng = lng.subtract(min_x).unitScale(0, x_step).int()
        lat = lat.subtract(min_y).unitScale(0, x_step).int()
        index_image = lat.multiply(cols).add(lng)

        # Get the NDFI data.
        diff = self._ndfi_delta(asset_id).select(0)
        masked = diff.mask(diff.mask().And(diff.lte(MAX_NDFI)))

        # Aggregate each cell.
        count_reducer = ee.call('Reducer.count')
        sum_reducer = ee.call('Reducer.sum')
        results = []
        for y in range(rows):
            for x in range(cols):
                # Compose the reduction query for the cell.
                index = y * cols + x
                cell_img = masked.mask(masked.mask().And(index_image.eq(index)))
                count_query = cell_img.reduceRegion(
                    count_reducer, rect, None, MODIS_CRS, MODIS_TRANSFORM)
                sum_query = cell_img.reduceRegion(
                    sum_reducer, rect, None, MODIS_CRS, MODIS_TRANSFORM)
                # Run the aggregations.
                count = ee.data.getValue(
                    {'json': ee.serializer.toJSON(count_query, False)})
                sum = ee.data.getValue(
                    {'json': ee.serializer.toJSON(sum_query, False)})
                results.append([count, sum])

        # Repackages the results in a backward-compatible form:
        counts = [int(i[0]['ndfi']) for i in results]
        sums = [int(i[1]['ndfi']) for i in results]
        return {
            'properties': {
                'ndfiSum': {
                    'values': {
                        'count': counts,
                        'sum': sums
                    },
                    'type': 'DataDictionary'
                }
            }
        }
 def maskInvalid(img):
     valid = img.select('num_observations_1km').neq(0).And(
         img.select('num_observations').neq(0)).And(
         img.select('state_1km').right_shift(6).mod(4).neq(0)).And(
         img.select('sur_refl_.*').reduce(ee.call('Reducer.min')).gt(0))
     return img.mask(valid).select(BAND_SRCS, BAND_DSTS)
Example #21
0
def detectWater(image, sensor=None, applyCloudMask=True, applySnowMask=True, applySlopeMask=False, applyNDVIMask=False, applyTemperatureMask=False, applyBareRockMask=False):
    # get the sensor information from the scene if it isnt already known
    if not sensor:
        try:
            sensor = getSensorInformation(image)
        except (GoogleEarthEngineError) as e:
            sensor = sensors[2]  # use landsat 8 as the default value if you cant get the sensor information
            print e
    # add a band for no data areas
    image = image.addBands(ee.call('Image.not', image.expression("b('" + sensor['TIR'] + "')>0").mask()).clip(image.geometry()).select([sensor['TIR']], ["isEmpty"]))
    # add a band for the ndvi
    image = image.addBands(image.normalizedDifference([sensor['NIR'], sensor['Red']]).select(["nd"], ["ndvi"]))
    # compute the terrain if required
    if applySnowMask or applySlopeMask:
      terrain = ee.call('Terrain', ee.Image('srtm90_v4')).clip(image.geometry())
    # MASKS SECTION
    # add a band for the cloud mask
    if applyCloudMask:
        # add a band for areas where the temperature is low enough for cloud
        image = image.addBands(image.expression("b('" + sensor['TIR'] + "')<" + str(lowerSceneTempThreshold)).select([sensor['TIR']], ["cloud_temp_ok"]))
        # add a band for areas where the ndvi is low enough for cloud
        image = image.addBands(image.expression("b('ndvi')<" + str(cloudNDVIThreshold)).select(["ndvi"], ["cloud_ndvi_ok"]))
        # add a band for areas where the hsv 654 is ok for cloud
        image = image.addBands(convertToHsv(image, sensor['SWIR1'] + "," + sensor['NIR'] + "," + sensor['Red']))
        image = image.addBands(image.expression("((b('h')>(-63.9767458759244*b('v'))+126.96415795606003)&&(b('h')<(-110.99212035041235*b('v'))+188.63866879085975)&&(b('h')<(603.2197202957016*b('v'))-37.718566327653065))||((b('h')>(25944.551568789888*b('v'))-33990.88089314458)&&(b('h')<(36.86094838605795*b('v'))+141.77916585585064)&&(b('h')>(-110.99212035041235*b('v'))+188.63866879085975))||((b('h')<(2.706669936469208*b('v'))+186.77647545634971)&&(b('h')<(106.69607879660673*b('v'))+119.64611493979173)&&(b('h')>(36.86094838605795*b('v'))+141.77916585585064))||((b('h')<(81.81592861333533*b('v'))+135.70749532282187)&&(b('h')<(176.29751158778367*b('v'))+97.58713048968069)&&(b('h')>(106.69607879660673*b('v'))+119.64611493979172))||((b('h')>(81.81592861333533*b('v'))+135.70749532282187)&&(b('h')<(63.82071478662236*b('v'))+147.32430519799433)&&(b('h')<(109.53269473723807*b('v'))+124.52464673608978))||((b('h')<(8.966684960846429*b('v'))+182.73532290022047)&&(b('h')>(2.706669936469208*b('v'))+186.77647545634971)&&(b('h')<(1.3539150425983255*b('v'))+188.55869231281008))||((b('h')>(-634.8621367931978*b('v'))+267.8746186626148)&&(b('h')>(-14.34796731410254*b('v'))+61.86139794741938)&&(b('h')<(-63.9767458759244*b('v'))+126.96415795606003))||((b('h')<(-14.34796731410254*b('v'))+61.86139794741938)&&(b('h')>(-114.63190817087207*b('v'))+95.15607300578044)&&(b('h')>(1.1835799314009672*b('v'))+41.48719930323337))").select(["h"], ["cloud_hsv_654_ok"]))
        # add a band for areas where the hsv 432 is ok for cloud
        image = image.addBands(convertToHsv(image, sensor['Red'] + "," + sensor['Green'] + "," + sensor['Blue']), None, True)
        image = image.addBands(image.expression("((b('h')<(490.8082696335494*b('v'))+79.95677350110236)&&(b('h')>(-91.39829801435039*b('v'))+235.14099783080272)&&(b('h')>(1426.1409800726237*b('v'))-262.4401146190752))||((b('h')<(-91.39829801435039*b('v'))+235.14099783080272)&&(b('h')>(-1462.3727682296092*b('v'))+600.5673285499772)&&(b('h')>(43.0109637714587*b('v'))+191.06997379295458))||((b('h')<(193.9753887166038*b('v'))+188.61827286211468)&&(b('h')<(1426.1409800726237*b('v'))-262.4401146190752)&&(b('h')>(276.2440822973692*b('v'))+114.59591064872176))||((b('h')>(193.9753887166038*b('v'))+188.61827286211468)&&(b('h')<(4.324914848340889*b('v'))+359.25883441225426)&&(b('h')<(31201.82786617771*b('v'))-11162.414442104384))||((b('h')>(-7586.058735191112*b('v'))+2692.54129818123)&&(b('h')>(336.94797401444475*b('v'))+59.976768604942734)&&(b('h')<(276.2440822973692*b('v'))+114.59591064872174))||((b('h')<(336.94797401444475*b('v'))+59.97676860494278)&&(b('h')>(-120.38370866692127*b('v'))+211.93362163645094)&&(b('h')<(-607.9749503868337*b('v'))+910.1838635444369))||((b('h')>(-17.228323217984126*b('v'))+64.21096650884475)&&(b('h')<(-120.38370866692127*b('v'))+211.93362163645097)&&(b('h')>(-1052.9083931253128*b('v'))+521.7820790922744))||((b('h')>(-4.838229766817528*b('v'))+46.46785504723803)&&(b('h')<(-17.228323217984126*b('v'))+64.21096650884475)&&(b('h')>(-113.58235093045452*b('v'))+106.78088838272488))||((b('h')<(-4.838229766817528*b('v'))+46.46785504723803)&&(b('h')>(-2611.3799432671185*b('v'))+1492.1408309694498)&&(b('h')>(18.14135712802163*b('v'))+13.560163654610307))||((b('h')<(-7243.576669583244*b('v'))+10412.632039788463)&&(b('h')<(18.14135712802163*b('v'))+13.560163654610307)&&(b('h')>(-27.301183714976023*b('v'))+39.11251888901155))||((b('h')<(-27.301183714976023*b('v'))+39.11251888901155)&&(b('h')<(50.865139764507866*b('v'))-4.840429776768538)&&(b('h')>(-17.46625242379369*b('v'))+24.974636828442005))||((b('h')<(-17.46625242379369*b('v'))+24.974636828442)&&(b('h')>(10000000*b('v'))-4363287.073623016)&&(b('h')>(-0.13337531632500316*b('v'))+0.058329326076244706))").select(["h"], ["cloud_hsv_432_ok"]))
        cloudMask = image.expression("(b('cloud_temp_ok'))||(b('cloud_ndvi_ok')==1&&b('cloud_hsv_654_ok')==1&&b('cloud_hsv_432_ok')==1)").select(["cloud_temp_ok"], ["isCloud"])
        image = image.addBands(cloudMask.convolve(ee.Kernel.fixed(5, 5, [[0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0]])).expression("b('isCloud')>0"))
    else:
        image = image.addBands(ee.Image(0).clip(image.geometry()).select([0], ["isCloud"]))
    # add a band for the snow mask
    if applySnowMask:
      # add a band for areas which are higher than the snowNorthThreshold degrees latitude
      image = image.addBands(ee.Image.pixelLonLat().expression("b('latitude')>" + str(snowNorthThreshold)).select(["latitude"], ["snow_lat_ok"]))
      # add a band for areas which are higher than the snowAltitudinalThreshold
      image = image.addBands(terrain.expression("b('elevation')>" + str(snowAltitudinalThreshold)).mask(1).select(["elevation"], ["snow_alt_ok"]))
      # add a band for areas where the hsv 432 is ok for snow
      image = image.addBands(convertToHsv(image, sensor['Red'] + "," + sensor['Green'] + "," + sensor['Blue']), None, True)
      image = image.addBands(image.expression("b('v')>" + str(snowValueThreshold)).select(["v"], ["snow_v_ok"]))
      image = image.addBands(image.expression("b('s')<" + str(snowSatThreshold)).select(["s"], ["snow_s_ok"]))
      # add a band for areas where the temperature is low enough for snow
      image = image.addBands(image.expression("b('" + sensor['TIR'] + "')<" + str(snowTemperatureThreshold)).select([sensor['TIR']], ["snow_temp_ok"]))
      image = image.addBands(image.expression("b('snow_v_ok')==1&&b('snow_s_ok')==1&&b('snow_temp_ok')==1&&(b('snow_lat_ok')==1||b('snow_alt_ok')==1)").select(["snow_v_ok"], ["isSnow"]))
    # add a band for the slope mask
    if applySlopeMask:
        slope_radians = terrain.select(['slope']).expression("(b('slope')*" + str(math.pi) + ")/180")
        slope_areas = slope_radians.expression("(b('slope')>" + str(slopeMaskThreshold) + ")")
        image = image.addBands(slope_areas.select(["slope"], ["isSteep"]))
    # add a band for the ndvi mask
    if applyNDVIMask:
        image = image.addBands(image.expression("b('ndvi')>" + str(ndviMaskThreshold)).select(["ndvi"], ["isGreen"]))
    # add a band for the temperature mask
    if applyTemperatureMask:
        image = image.addBands(ee.call('Image.not', image.expression("b('" + sensor['TIR'] + "')<" + str(upperSceneTempThreshold) + "&&b('" + sensor['TIR'] + "')>" + str(lowerSceneTempThreshold))).select([sensor['TIR']], ["isTooHotOrCold"]))
    # add a band for the barerock mask
    if applyBareRockMask:
        landsat_collection = ee.ImageCollection("LANDSAT/LC8_L1T_TOA").filterDate(datetime.datetime(2013, 4, 1), datetime.datetime(2014, 4, 1)).filterBounds(image.geometry())
        
        #identify the bare rock confusion domain from the scene
        image = image.addBands(image.expression("((b('" + sensor['Green'] + "')<(0.8255813953488379*b('" + sensor['NIR'] + "'))+0.026636991279069665)&&(b('" + sensor['Green'] + "')>(-0.2499999999999994*b('" + sensor['NIR'] + "'))+0.04058593750000021)&&(b('" + sensor['Green'] + "')>(1.6000000000000014*b('" + sensor['NIR'] + "'))+0.006187499999999648))||((b('" + sensor['Green'] + "')<(-0.40000000000000063*b('" + sensor['NIR'] + "'))+0.059000000000000295)&&(b('" + sensor['Green'] + "')<(1.6000000000000014*b('" + sensor['NIR'] + "'))+0.006187499999999645)&&(b('" + sensor['Green'] + "')>(0.5523809523809521*b('" + sensor['NIR'] + "'))+0.025666666666666657))||((b('" + sensor['Green'] + "')<(1*b('" + sensor['NIR'] + "'))+0.022031249999999863)&&(b('" + sensor['Green'] + "')>(-0.40000000000000063*b('" + sensor['NIR'] + "'))+0.059000000000000295)&&(b('" + sensor['Green'] + "')>(3.264705882352946*b('" + sensor['NIR'] + "'))-0.06926470588235396))||((b('" + sensor['Green'] + "')>(0.4457478005865104*b('" + sensor['NIR'] + "'))+0.02939882697947215)&&(b('" + sensor['Green'] + "')<(0.13355048859934848*b('" + sensor['NIR'] + "'))+0.05695999592833889)&&(b('" + sensor['Green'] + "')<(3.264705882352946*b('" + sensor['NIR'] + "'))-0.06926470588235398))||((b('" + sensor['Green'] + "')<(-0.09328358208955286*b('" + sensor['NIR'] + "'))+0.07698519123134354)&&(b('" + sensor['Green'] + "')<(1.6923076923076947*b('" + sensor['NIR'] + "'))-0.005877403846154289)&&(b('" + sensor['Green'] + "')>(0.13355048859934848*b('" + sensor['NIR'] + "'))+0.05695999592833889))||((b('" + sensor['Green'] + "')<(-1.3106060606060648*b('" + sensor['NIR'] + "'))+0.18445194128787978)&&(b('" + sensor['Green'] + "')<(1.088235294117649*b('" + sensor['NIR'] + "'))+0.022155330882352706)&&(b('" + sensor['Green'] + "')>(-0.09328358208955286*b('" + sensor['NIR'] + "'))+0.07698519123134354))||((b('" + sensor['Green'] + "')<(0.6903765690376558*b('" + sensor['NIR'] + "'))+0.04907296025104613)&&(b('" + sensor['Green'] + "')>(-1.3106060606060648*b('" + sensor['NIR'] + "'))+0.18445194128787978)&&(b('" + sensor['Green'] + "')>(3.1588785046728995*b('" + sensor['NIR'] + "'))-0.2101197429906553))||((b('" + sensor['Green'] + "')<(-1.0582524271844669*b('" + sensor['NIR'] + "'))+0.2326790048543696)&&(b('" + sensor['Green'] + "')<(3.1588785046728995*b('" + sensor['NIR'] + "'))-0.21011974299065528)&&(b('" + sensor['Green'] + "')>(0.3833865814696488*b('" + sensor['NIR'] + "'))+0.03490415335463261))||((b('" + sensor['Green'] + "')<(0.38314176245210746*b('" + sensor['NIR'] + "'))+0.0813326149425288)&&(b('" + sensor['Green'] + "')>(-1.0582524271844669*b('" + sensor['NIR'] + "'))+0.2326790048543696)&&(b('" + sensor['Green'] + "')>(5.78181818181818*b('" + sensor['NIR'] + "'))-0.7056931818181835))||((b('" + sensor['Green'] + "')<(-0.8358778625954201*b('" + sensor['NIR'] + "'))+0.2590428196564891)&&(b('" + sensor['Green'] + "')<(5.78181818181818*b('" + sensor['NIR'] + "'))-0.7056931818181834)&&(b('" + sensor['Green'] + "')>(0.3123028391167197*b('" + sensor['NIR'] + "'))+0.0446559542586751))||((b('" + sensor['Green'] + "')<(-1.4397905759162286*b('" + sensor['NIR'] + "'))+0.3718046465968591)&&(b('" + sensor['Green'] + "')<(0.7887323943662009*b('" + sensor['NIR'] + "'))+0.022205105633802222)&&(b('" + sensor['Green'] + "')>(-0.8358778625954201*b('" + sensor['NIR'] + "'))+0.2590428196564891))||((b('" + sensor['Green'] + "')<(-3.0842105263157817*b('" + sensor['NIR'] + "'))+0.6788486842105264)&&(b('" + sensor['Green'] + "')<(0.18749999999999953*b('" + sensor['NIR'] + "'))+0.11652343750000027)&&(b('" + sensor['Green'] + "')>(-1.4397905759162286*b('" + sensor['NIR'] + "'))+0.3718046465968591))||((b('" + sensor['Green'] + "')>(0.8272727272727277*b('" + sensor['NIR'] + "'))-0.051498579545454726)&&(b('" + sensor['Green'] + "')<(-0.985365853658536*b('" + sensor['NIR'] + "'))+0.3181097560975614)&&(b('" + sensor['Green'] + "')>(-3.0842105263157817*b('" + sensor['NIR'] + "'))+0.6788486842105262))||((b('" + sensor['Green'] + "')<(-1.8872180451127856*b('" + sensor['NIR'] + "'))+0.5020030545112799)&&(b('" + sensor['Green'] + "')<(0.6805555555555519*b('" + sensor['NIR'] + "'))+0.03177951388888953)&&(b('" + sensor['Green'] + "')>(-0.985365853658536*b('" + sensor['NIR'] + "'))+0.3181097560975614))||((b('" + sensor['Green'] + "')<(-0.6589147286821698*b('" + sensor['NIR'] + "'))+0.27707000968992285)&&(b('" + sensor['Green'] + "')>(-1.8872180451127856*b('" + sensor['NIR'] + "'))+0.5020030545112799)&&(b('" + sensor['Green'] + "')>(0.648*b('" + sensor['NIR'] + "'))-0.014943750000000033))||((b('" + sensor['Green'] + "')<(-12.333333333333234*b('" + sensor['NIR'] + "'))+2.88557291666665)&&(b('" + sensor['Green'] + "')<(0.5384615384615392*b('" + sensor['NIR'] + "'))+0.05780048076923067)&&(b('" + sensor['Green'] + "')>(-0.6589147286821698*b('" + sensor['NIR'] + "'))+0.27707000968992285))||((b('" + sensor['Green'] + "')<(-1.365384615384615*b('" + sensor['NIR'] + "'))+0.4760516826923084)&&(b('" + sensor['Green'] + "')>(-12.333333333333234*b('" + sensor['NIR'] + "'))+2.88557291666665)&&(b('" + sensor['Green'] + "')>(0.6287878787878773*b('" + sensor['NIR'] + "'))-0.010651041666666361))||((b('" + sensor['Green'] + "')>(-1.365384615384615*b('" + sensor['NIR'] + "'))+0.4760516826923084)&&(b('" + sensor['Green'] + "')>(1.2953020134228208*b('" + sensor['NIR'] + "'))-0.1733221476510075)&&(b('" + sensor['Green'] + "')<(-0.06557377049180299*b('" + sensor['NIR'] + "'))+0.19049948770491826))||((b('" + sensor['Green'] + "')>(8.062500000000188*b('" + sensor['NIR'] + "'))-1.9824902343750537)&&(b('" + sensor['Green'] + "')<(1.2953020134228208*b('" + sensor['NIR'] + "'))-0.1733221476510075)&&(b('" + sensor['Green'] + "')>(0.48120300751879613*b('" + sensor['NIR'] + "'))+0.02536889097744384))||((b('" + sensor['Green'] + "')<(-2.323943661971829*b('" + sensor['NIR'] + "'))+0.7942605633802824)&&(b('" + sensor['Green'] + "')<(0.6196581196581197*b('" + sensor['NIR'] + "'))+0.03996260683760686)&&(b('" + sensor['Green'] + "')>(-0.06557377049180299*b('" + sensor['NIR'] + "'))+0.19049948770491826))||((b('" + sensor['Green'] + "')<(-0.20600858369098746*b('" + sensor['NIR'] + "'))+0.2515396995708159)&&(b('" + sensor['Green'] + "')>(-2.323943661971829*b('" + sensor['NIR'] + "'))+0.7942605633802824)&&(b('" + sensor['Green'] + "')>(0.7222222222222233*b('" + sensor['NIR'] + "'))-0.020112847222222535))||((b('" + sensor['Green'] + "')<(0.4918566775244293*b('" + sensor['NIR'] + "'))+0.07271172638436507)&&(b('" + sensor['Green'] + "')>(-0.20600858369098746*b('" + sensor['NIR'] + "'))+0.2515396995708159)&&(b('" + sensor['Green'] + "')>(2.689189189189159*b('" + sensor['NIR'] + "'))-0.5957580236486406))||((b('" + sensor['Green'] + "')<(-3.0000000000000853*b('" + sensor['NIR'] + "'))+1.135000000000028)&&(b('" + sensor['Green'] + "')<(2.689189189189159*b('" + sensor['NIR'] + "'))-0.5957580236486406)&&(b('" + sensor['Green'] + "')>(1.252525252525252*b('" + sensor['NIR'] + "'))-0.17530934343434348))||((b('" + sensor['Green'] + "')<(0.12631578947368488*b('" + sensor['NIR'] + "'))+0.18391611842105268)&&(b('" + sensor['Green'] + "')>(-3.0000000000000853*b('" + sensor['NIR'] + "'))+1.135000000000028)&&(b('" + sensor['Green'] + "')>(0.6000000000000006*b('" + sensor['NIR'] + "'))+0.025749999999999856))").select([sensor["Green"]], ["bareRockConfusionArea"]))
        image = image.addBands(landsat_collection.select(["B10"], ["rock_temp_ok"]).max().expression('b("rock_temp_ok")>' + str(annualMaxTempThreshold)))
        landsat_collection_ndvi = landsat_collection.map(landsatNDVI)
        isrock = landsat_collection_ndvi.max().clip(image.geometry())
        image = image.addBands(isrock.expression('b("nd")<' + str(annualMaxNDVIThreshold)).select(["nd"], ["rock_ndvi_ok"]))
        image = image.addBands(image.expression("b('bareRockConfusionArea')==1&&b('rock_temp_ok')==1&&b('rock_ndvi_ok')==1").select(['bareRockConfusionArea'], ['isRock']))
    # add a band for the total mask - this adds all bands with the prefix 'is*' 
    image = image.addBands(ee.call('Image.not', image.select(["is.*"]).reduce(ee.Reducer.anyNonZero())).select(["any"], ["detectArea"]))
    # detect water
    for i in range(len(waterDetectExpressions)):
        waterDetectExpression = waterDetectExpressions[i]
        # convert the band descriptions to actual band names according to the sensor, e.g. "SWIR2,NIR,Red" -> "B7,B5,B4" for landsat
        bands = getGEEBandNames(waterDetectExpression['bands'], sensor)
        # convert to hsv
        hsv = convertToHsv(image, bands)
        detection = hsv.expression(waterDetectExpression['expression'])
        image = image.addBands(detection.select([0], ["detectExpression" + str(i + 1)]))
    # add a band for the total detections - this combines all the bands with the prefix 'detect'
    image = image.addBands(image.select(["detect.*"]).reduce(ee.Reducer.allNonZero()).select(["all"], ["water"]))
    # create a final water detection layer with 0-not water (grey), 1=no data (black), 2=cloud(white), 3=water(blue)
    detection = image.expression("b('isEmpty')+((b('isCloud')||b('isSnow'))*2)+(b('water')*3)").select(["isEmpty"], ["class"])
    return detection  # 0-not water, 1=no data, 2=cloud, 3=water
Example #22
0
def mgrs_export_tiles(study_area_coll_id, mgrs_coll_id,
                      study_area_property=None, study_area_features=[],
                      mgrs_tiles=[], mgrs_skip_list=[],
                      utm_zones=[], wrs2_tiles=[],
                      mgrs_property='mgrs', utm_property='utm',
                      wrs2_property='wrs2'):
    """Select MGRS tiles and metadata that intersect the study area geometry

    Parameters
    ----------
    study_area_coll_id : str
        Study area feature collection asset ID.
    mgrs_coll_id : str
        MGRS feature collection asset ID.
    study_area_property : str, optional
        Property name to use for inList() filter call of study area collection.
        Filter will only be applied if both 'study_area_property' and
        'study_area_features' parameters are both set.
    study_area_features : list, optional
        List of study area feature property values to filter on.
    mgrs_tiles : list, optional
        User defined MGRS tile subset.
    mgrs_skip_list : list, optional
        User defined list MGRS tiles to skip.
    utm_zones : list, optional
        User defined UTM zone subset.
    wrs2_tiles : list, optional
        User defined WRS2 tile subset.
    mgrs_property : str, optional
        MGRS property in the MGRS feature collection (the default is 'mgrs').
    utm_property : str, optional
        UTM zone property in the MGRS feature collection (the default is 'wrs2').
    wrs2_property : str, optional
        WRS2 property in the MGRS feature collection (the default is 'wrs2').

    Returns
    ------
    list of dicts: export information

    """
    # Build and filter the study area feature collection
    logging.debug('Building study area collection')
    logging.debug(f'  {study_area_coll_id}')
    study_area_coll = ee.FeatureCollection(study_area_coll_id)
    if (study_area_coll_id in ['TIGER/2018/States'] and
            'CONUS' in [x.upper() for x in study_area_features]):
        study_area_coll_id = 'projects/openet/featureCollections/boundaries/tiger_2018_conus'
        study_area_coll = ee.FeatureCollection(study_area_coll_id)
        study_area_property == 'STUSPS'
        study_area_features = ['CONUS']
    elif (study_area_property == 'STUSPS' and
            'CONUS' in [x.upper() for x in study_area_features]):
        # Excluding AK, HI, AS, GU, PR, MP, VI
        # Dropping some other states also since they aren't needed in order
        #   to intersect all the MGRS tiles in the CONUS (CT, DE, DC, RI, VT)
        study_area_features = [
            'AL', 'AR', 'AZ', 'CA', 'CO', 'FL', 'GA',
            'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME',
            'MI', 'MN', 'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ',
            'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'SC', 'SD',
            'TN', 'TX', 'UT', 'VA', 'WA', 'WI', 'WV', 'WY']
    # elif (study_area_property == 'STUSPS' and
    #         'WESTERN11' in [x.upper() for x in study_area_features]):
    #     study_area_features = [
    #         'AZ', 'CA', 'CO', 'ID', 'MT', 'NM', 'NV', 'OR', 'UT', 'WA', 'WY']
    study_area_features = sorted(list(set(study_area_features)))

    if study_area_property and study_area_features:
        logging.debug('  Filtering study area collection')
        logging.debug(f'  Property: {study_area_property}')
        logging.debug(f'  Features: {",".join(study_area_features)}')
        study_area_coll = study_area_coll.filter(
            ee.Filter.inList(study_area_property, study_area_features))

    logging.debug('Building MGRS tile list')
    tiles_coll = ee.FeatureCollection(mgrs_coll_id) \
        .filterBounds(study_area_coll.geometry())

    # Filter collection by user defined lists
    if utm_zones:
        logging.debug(f'  Filter user UTM Zones:    {utm_zones}')
        tiles_coll = tiles_coll.filter(ee.Filter.inList(utm_property, utm_zones))
    if mgrs_skip_list:
        logging.debug(f'  Filter MGRS skip list:    {mgrs_skip_list}')
        tiles_coll = tiles_coll.filter(
            ee.Filter.inList(mgrs_property, mgrs_skip_list).Not())
    if mgrs_tiles:
        logging.debug(f'  Filter MGRS tiles/zones:  {mgrs_tiles}')
        # Allow MGRS tiles to be subsets of the full tile code
        #   i.e. mgrs_tiles = 10TE, 10TF
        mgrs_filters = [
            ee.Filter.stringStartsWith(mgrs_property, mgrs_id.upper())
            for mgrs_id in mgrs_tiles]
        tiles_coll = tiles_coll.filter(ee.call('Filter.or', mgrs_filters))

    def drop_geometry(ftr):
        return ee.Feature(None).copyProperties(ftr)

    logging.debug('  Requesting tile/zone info')
    tiles_info = utils.get_info(tiles_coll.map(drop_geometry))

    # Constructed as a list of dicts to mimic other interpolation/export tools
    tiles_list = []
    for tile_ftr in tiles_info['features']:
        tiles_list.append({
            'index': tile_ftr['properties']['mgrs'].upper(),
            'wrs2_tiles': sorted(utils.wrs2_str_2_set(
                tile_ftr['properties'][wrs2_property])),
        })

    # Apply the user defined WRS2 tile list
    if wrs2_tiles:
        logging.debug(f'  Filter WRS2 tiles: {wrs2_tiles}')
        for tile in tiles_list:
            tile['wrs2_tiles'] = sorted(list(
                set(tile['wrs2_tiles']) & set(wrs2_tiles)))

    # Only return export tiles that have intersecting WRS2 tiles
    export_list = [
        tile for tile in sorted(tiles_list, key=lambda k: k['index'])
        if tile['wrs2_tiles']]

    return export_list
Example #23
0
def detectWater(image):
    applyCloudMask = True
    applySnowMask = True
    applySlopeMask = False
    applyNDVIMask = False
    applyTemperatureMask = False
    applyBareRockMask = False
    # add a band for no data areas
    image = image.addBands(ee.call('Image.not', image.expression("b('" + sensor['TIR'] + "')>0").mask()).clip(image.geometry()).select([sensor['TIR']], ["isEmpty"]))
    # add a band for the ndvi
    image = image.addBands(image.normalizedDifference([sensor['NIR'], sensor['Red']]).select(["nd"], ["ndvi"]))
    # compute the terrain if required
    if applySnowMask or applySlopeMask:
      terrain = ee.call('Terrain', ee.Image('srtm90_v4')).clip(image.geometry())
    # MASKS SECTION
    # add a band for the cloud mask
    if applyCloudMask:
        # add a band for areas where the temperature is low enough for cloud
        image = image.addBands(image.expression("b('" + sensor['TIR'] + "')<" + str(lowerSceneTempThreshold)).select([sensor['TIR']], ["cloud_temp_ok"]))
        # add a band for areas where the ndvi is low enough for cloud
        image = image.addBands(image.expression("b('ndvi')<" + str(cloudNDVIThreshold)).select(["ndvi"], ["cloud_ndvi_ok"]))
        # add a band for areas where the hsv 654 is ok for cloud
        image = image.addBands(convertToHsv(image, sensor['SWIR1'] + "," + sensor['NIR'] + "," + sensor['Red']))
        image = image.addBands(image.expression("((b('h')>(-63.9767458759244*b('v'))+126.96415795606003)&&(b('h')<(-110.99212035041235*b('v'))+188.63866879085975)&&(b('h')<(603.2197202957016*b('v'))-37.718566327653065))||((b('h')>(25944.551568789888*b('v'))-33990.88089314458)&&(b('h')<(36.86094838605795*b('v'))+141.77916585585064)&&(b('h')>(-110.99212035041235*b('v'))+188.63866879085975))||((b('h')<(2.706669936469208*b('v'))+186.77647545634971)&&(b('h')<(106.69607879660673*b('v'))+119.64611493979173)&&(b('h')>(36.86094838605795*b('v'))+141.77916585585064))||((b('h')<(81.81592861333533*b('v'))+135.70749532282187)&&(b('h')<(176.29751158778367*b('v'))+97.58713048968069)&&(b('h')>(106.69607879660673*b('v'))+119.64611493979172))||((b('h')>(81.81592861333533*b('v'))+135.70749532282187)&&(b('h')<(63.82071478662236*b('v'))+147.32430519799433)&&(b('h')<(109.53269473723807*b('v'))+124.52464673608978))||((b('h')<(8.966684960846429*b('v'))+182.73532290022047)&&(b('h')>(2.706669936469208*b('v'))+186.77647545634971)&&(b('h')<(1.3539150425983255*b('v'))+188.55869231281008))||((b('h')>(-634.8621367931978*b('v'))+267.8746186626148)&&(b('h')>(-14.34796731410254*b('v'))+61.86139794741938)&&(b('h')<(-63.9767458759244*b('v'))+126.96415795606003))||((b('h')<(-14.34796731410254*b('v'))+61.86139794741938)&&(b('h')>(-114.63190817087207*b('v'))+95.15607300578044)&&(b('h')>(1.1835799314009672*b('v'))+41.48719930323337))").select(["h"], ["cloud_hsv_654_ok"]))
        # add a band for areas where the hsv 432 is ok for cloud
        image = image.addBands(convertToHsv(image, sensor['Red'] + "," + sensor['Green'] + "," + sensor['Blue']), None, True)
        image = image.addBands(image.expression("((b('h')<(490.8082696335494*b('v'))+79.95677350110236)&&(b('h')>(-91.39829801435039*b('v'))+235.14099783080272)&&(b('h')>(1426.1409800726237*b('v'))-262.4401146190752))||((b('h')<(-91.39829801435039*b('v'))+235.14099783080272)&&(b('h')>(-1462.3727682296092*b('v'))+600.5673285499772)&&(b('h')>(43.0109637714587*b('v'))+191.06997379295458))||((b('h')<(193.9753887166038*b('v'))+188.61827286211468)&&(b('h')<(1426.1409800726237*b('v'))-262.4401146190752)&&(b('h')>(276.2440822973692*b('v'))+114.59591064872176))||((b('h')>(193.9753887166038*b('v'))+188.61827286211468)&&(b('h')<(4.324914848340889*b('v'))+359.25883441225426)&&(b('h')<(31201.82786617771*b('v'))-11162.414442104384))||((b('h')>(-7586.058735191112*b('v'))+2692.54129818123)&&(b('h')>(336.94797401444475*b('v'))+59.976768604942734)&&(b('h')<(276.2440822973692*b('v'))+114.59591064872174))||((b('h')<(336.94797401444475*b('v'))+59.97676860494278)&&(b('h')>(-120.38370866692127*b('v'))+211.93362163645094)&&(b('h')<(-607.9749503868337*b('v'))+910.1838635444369))||((b('h')>(-17.228323217984126*b('v'))+64.21096650884475)&&(b('h')<(-120.38370866692127*b('v'))+211.93362163645097)&&(b('h')>(-1052.9083931253128*b('v'))+521.7820790922744))||((b('h')>(-4.838229766817528*b('v'))+46.46785504723803)&&(b('h')<(-17.228323217984126*b('v'))+64.21096650884475)&&(b('h')>(-113.58235093045452*b('v'))+106.78088838272488))||((b('h')<(-4.838229766817528*b('v'))+46.46785504723803)&&(b('h')>(-2611.3799432671185*b('v'))+1492.1408309694498)&&(b('h')>(18.14135712802163*b('v'))+13.560163654610307))||((b('h')<(-7243.576669583244*b('v'))+10412.632039788463)&&(b('h')<(18.14135712802163*b('v'))+13.560163654610307)&&(b('h')>(-27.301183714976023*b('v'))+39.11251888901155))||((b('h')<(-27.301183714976023*b('v'))+39.11251888901155)&&(b('h')<(50.865139764507866*b('v'))-4.840429776768538)&&(b('h')>(-17.46625242379369*b('v'))+24.974636828442005))||((b('h')<(-17.46625242379369*b('v'))+24.974636828442)&&(b('h')>(10000000*b('v'))-4363287.073623016)&&(b('h')>(-0.13337531632500316*b('v'))+0.058329326076244706))").select(["h"], ["cloud_hsv_432_ok"]))
        cloudMask = image.expression("(b('cloud_temp_ok'))||(b('cloud_ndvi_ok')==1&&b('cloud_hsv_654_ok')==1&&b('cloud_hsv_432_ok')==1)").select(["cloud_temp_ok"], ["isCloud"])
        image = image.addBands(cloudMask.convolve(ee.Kernel.fixed(5, 5, [[0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0]])).expression("b('isCloud')>0"))
    else:
        image = image.addBands(ee.Image(0).clip(image.geometry()).select([0], ["isCloud"]))
    # add a band for the snow mask
    if applySnowMask:
      # add a band for areas which are higher than the snowNorthThreshold degrees latitude
      image = image.addBands(ee.Image.pixelLonLat().expression("b('latitude')>" + str(snowNorthThreshold)).select(["latitude"], ["snow_lat_ok"]))
      # add a band for areas which are higher than the snowAltitudinalThreshold
      image = image.addBands(terrain.expression("b('elevation')>" + str(snowAltitudinalThreshold)).mask().select(["elevation"], ["snow_alt_ok"]))
      # add a band for areas where the hsv 432 is ok for snow
      image = image.addBands(convertToHsv(image, sensor['Red'] + "," + sensor['Green'] + "," + sensor['Blue']), None, True)
      image = image.addBands(image.expression("b('v')>" + str(snowValueThreshold)).select(["v"], ["snow_v_ok"]))
      # add a band for areas where the temperature is low enough for snow
      image = image.addBands(image.expression("b('" + sensor['TIR'] + "')<" + str(snowTemperatureThreshold)).select([sensor['TIR']], ["snow_temp_ok"]))
      image = image.addBands(image.expression("b('snow_v_ok')==1&&b('snow_temp_ok')==1&&(b('snow_lat_ok')==1||b('snow_alt_ok')==1)").select(["snow_v_ok"], ["isSnow"]))
    # add a band for the slope mask
    if applySlopeMask:
        slope_radians = terrain.select(['slope']).expression("(b('slope')*" + str(math.pi) + ")/180")
        slope_areas = slope_radians.expression("(b('slope')>" + str(slopeMaskThreshold) + ")")
        image = image.addBands(slope_areas.select(["slope"], ["isSteep"]))
    # add a band for the ndvi mask
    if applyNDVIMask:
        image = image.addBands(image.expression("b('ndvi')>" + str(ndviMaskThreshold)).select(["ndvi"], ["isGreen"]))
    # add a band for the temperature mask
    if applyTemperatureMask:
        image = image.addBands(ee.call('Image.not', image.expression("b('" + sensor['TIR'] + "')<" + str(upperSceneTempThreshold) + "&&b('" + sensor['TIR'] + "')>" + str(lowerSceneTempThreshold))).select([sensor['TIR']], ["isTooHotOrCold"]))
    # add a band for the total mask - this adds all bands with the prefix 'is' 
#     maskbands = ["isCloud", "isSnow", "isSteep", "isGreen", "isTooHotOrCold"]
    maskbands = ["isCloud", "isSnow" ]
    image = image.addBands(ee.call('Image.not', image.select(maskbands).reduce(ee.Reducer.anyNonZero())).select(["any"], ["detectArea"]))
    # detect water
    for i in range(len(waterDetectExpressions)):
        waterDetectExpression = waterDetectExpressions[i]
        # convert the band descriptions to actual band names according to the sensor, e.g. "SWIR2,NIR,Red" -> "B7,B5,B4" for landsat
        bands = getGEEBandNames(waterDetectExpression['bands'], sensor)
        # convert to hsv
        hsv = convertToHsv(image, bands)
        detection = hsv.expression(waterDetectExpression['expression'])
        image = image.addBands(detection.select([0], ["detectExpression" + str(i + 1)]))
    # add a band for the total detections - this combines all the bands with the prefix 'detect'
    detectbands = ["detectExpression1", "detectExpression2"]
    image = image.addBands(image.select(detectbands).reduce(ee.Reducer.allNonZero()).select(["all"], ["water"]))
    water = image.select(["water"]).mask(image.select(["water"]))
    waterArea = water.multiply(ee.Image.pixelArea())
    totalArea = waterArea.reduceRegion(ee.Reducer.sum(), poly, 30, None, None, True);
    water = water.set({'totalArea':totalArea})
    url = water.getDownloadURL({'scale': 30, 'crs': 'EPSG:4326', 'region': JSON.stringify(poly.coordinates)}) 
    water = water.set({'download_url':url}) 
    return water
Example #24
0
    def testDynamicClasses(self):
        """Verifies dynamic class initialization."""

        # Use a custom set of known functions.
        def MockSend(path, unused_params, unused_method=None, unused_raw=None):
            if path == '/algorithms':
                return {
                    'Array': {
                        'type':
                        'Algorithm',
                        'args': [{
                            'name': 'values',
                            'type': 'Serializable',
                            'description': ''
                        }],
                        'description':
                        '',
                        'returns':
                        'Array'
                    },
                    'Array.cos': {
                        'type':
                        'Algorithm',
                        'args': [{
                            'type': 'Array',
                            'description': '',
                            'name': 'input'
                        }],
                        'description':
                        '',
                        'returns':
                        'Array'
                    },
                    'Kernel.circle': {
                        'returns':
                        'Kernel',
                        'args': [{
                            'type': 'float',
                            'description': '',
                            'name': 'radius',
                        }, {
                            'default': 1.0,
                            'type': 'float',
                            'optional': True,
                            'description': '',
                            'name': 'scale'
                        }, {
                            'default': True,
                            'type': 'boolean',
                            'optional': True,
                            'description': '',
                            'name': 'normalize'
                        }],
                        'type':
                        'Algorithm',
                        'description':
                        ''
                    },
                    'Reducer.mean': {
                        'returns': 'Reducer',
                        'args': []
                    },
                    'fakeFunction': {
                        'returns':
                        'Array',
                        'args': [{
                            'type': 'Reducer',
                            'description': '',
                            'name': 'kernel',
                        }]
                    }
                }

        ee.data.send_ = MockSend

        ee.Initialize(None)

        # Verify that the expected classes got generated.
        self.assertTrue(hasattr(ee, 'Array'))
        self.assertTrue(hasattr(ee, 'Kernel'))
        self.assertTrue(hasattr(ee.Array, 'cos'))
        self.assertTrue(hasattr(ee.Kernel, 'circle'))

        # Try out the constructors.
        kernel = ee.ApiFunction('Kernel.circle').call(1, 2)
        self.assertEquals(kernel, ee.Kernel.circle(1, 2))

        array = ee.ApiFunction('Array').call([1, 2])
        self.assertEquals(array, ee.Array([1, 2]))
        self.assertEquals(array, ee.Array(ee.Array([1, 2])))

        # Try out the member function.
        self.assertEquals(
            ee.ApiFunction('Array.cos').call(array),
            ee.Array([1, 2]).cos())

        # Test argument promotion.
        f1 = ee.ApiFunction('Array.cos').call([1, 2])
        f2 = ee.ApiFunction('Array.cos').call(ee.Array([1, 2]))
        self.assertEquals(f1, f2)
        self.assertTrue(isinstance(f1, ee.Array))

        f3 = ee.call('fakeFunction', 'mean')
        f4 = ee.call('fakeFunction', ee.Reducer.mean())
        self.assertEquals(f3, f4)

        try:
            ee.call('fakeFunction', 'moo')
            self.fail()
        except ee.EEException as e:
            self.assertTrue('Unknown algorithm: Reducer.moo' in str(e))