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
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)
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())
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
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))
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) )
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'])
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)
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
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
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
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))