I have code here that is supposed to take a feature collection of points (defined as "table") and get the values of those points where they intersect imagery from Landsat 5 surface reflectance filtered for 1985 in the month of March. This code works for other datasets in Earth Engine's catalog, but when exporting a CSV of the point values with Landsat imagery (Landsat 4 and 5 are what I tested), I get a CSV with no columns pertaining to the values for each band at each point. I am not sure what I am doing wrong.
The function I wrote ("getImgCoords") is essentially a nested map() using the amount of images in the image collection and the amount of coordinates per image as the length at which each loop will continue on for. For example, after I do all the clipping ("icClip"), I grab each image and check for how many points exist for each image ("getImgCoords"). Then for each point from that image, I use reduceRegion() to get the band values for that point and merge that dictionary with another dictionary of metadata from that image ("getMetadata"). I create a feature collection out of this. Try to disregard the variables "size_equal" and "end." I had to create a conditional statement so the function knows how to distinguish between a list of 2 sets of coordinates of length 2 (basically a list of a list) versus 1 set of coordinates of length 2.
This is the same code from another question regarding how to convert it over to python. I haven't changed the way I use "sequence" and "sequence2" to map things like that post, so apologies in advance!
Another note: I am getting this message whenever I try to print a variable in the "getImgCoords" function: "internal error: function argument not initialized." Not sure what this could mean since I do use my function arguments.
UPDATE: Just figured out it has something to do with my cloud masking. Removing the call for "cloudMaskL457" makes reduceRegion() work, but I am still unsure as to why.
var LANDSAT5 = LANDSAT5 = ee.ImageCollection("LANDSAT/LT05/C01/T1_SR");
var table = ee.FeatureCollection("users/adrianom/CAN_AK_Coordinates_sub1");
var SearchArea = table;
var SearchAreaGeom = SearchArea.geometry();
var cloudMaskL457 = function(image) {
var qa = image.select('pixel_qa');
// If the cloud bit (5) is set and the cloud confidence (7) is high
// or the cloud shadow bit is set (3), then it's a bad pixel.
var cloud = qa.bitwiseAnd(1 << 5)
.and(qa.bitwiseAnd(1 << 7))
.or(qa.bitwiseAnd(1 << 3))
// Remove edge pixels that don't occur in all bands
var mask2 = image.mask().reduce(ee.Reducer.min());
return image.updateMask(cloud.not()).updateMask(mask2);
};
var landsat5_filtered = LANDSAT5.filter(ee.Filter.calendarRange(1985, 1985, 'year'))
.filter(ee.Filter.calendarRange(3, 3, 'month')).filterBounds(SearchAreaGeom).map(cloudMaskL457);
var landsat5_select = landsat5_filtered.select(['B1', 'B2', 'B3', 'B4', 'B5', 'B7'],
['Blue', 'Green', 'Red', 'NIR', 'SWIR1', 'SWIR2']);
// Clip collection to search area, more precisely than filterBounds above.
var icClip = landsat5_select.map(function(img){
var i = img.clip(SearchAreaGeom);
return(i);
});
print(icClip);
var icClip_len = icClip.size();
var col_list = icClip.toList(icClip_len);
var sequence = ee.List.sequence(0, icClip_len.subtract(1));
var getImgCoords = sequence.map(function(i){
var img = ee.Image(col_list.get(i));
var coords = img.geometry().coordinates();
// Create a list of a list and flatten. This is ONLY made for use as a conditional statement.
var coords_list = ee.List([coords]);
var coords_flatten = coords_list.flatten();
// Boolean to check if the coordinates received are a set of coordinates (of length 2) or a list of a list of coordinates.
var size_equal = ee.Algorithms.IsEqual(coords_flatten.size(), ee.Number(2));
var coords_len = coords.size();
var end = ee.Algorithms.If(size_equal, coords_len.subtract(2), coords_len.subtract(1));
var sequence2 = ee.List.sequence(0, end);
var getMetadata = sequence2.map(function(j){
var coor = ee.Algorithms.If(ee.Algorithms.IsEqual(coords_flatten.size(), ee.Number(2)), coords, coords.get(j));
var geom = ee.Geometry.Point(coor);
var reduce = img.reduceRegion(ee.Reducer.mean(), geom, 30);
var dictionary = ee.Dictionary({
x: ee.List(coor).get(0),
y: ee.List(coor).get(1),
image_id: img.id(),
start_date: ee.Date(img.get('system:time_start')),
WRS_PATH: img.get('WRS_PATH'),
WRS_ROW: img.get('WRS_ROW')
});
var combine_dict = reduce.combine(dictionary);
var feature = ee.Feature(geom, combine_dict);
return(feature);
});
return(getMetadata);
});
var flat = getImgCoords.flatten();
var fc = ee.FeatureCollection(flat);
print(fc);
Export.table.toDrive({
collection: fc,
description: 'Landsat5',
folder: 'Landsat5',
fileFormat: 'CSV'
});
Best Answer
Here is an alternative approach that avoids nested map() calls, ee.Image.clip(), ee.Algorithms.If(), and accumulating results with a dictionary.
Modify the cloud masking function to add a band
cloud_flag
that indicates whether or not there is a cloud.Map the function across a filtered collection of Landsat images:
Next define a function that processes each image, extracting values that correspond with the sample points. This function is then mapped over all of the images to create a feature collection of sampled values.
The feature collection
extractValues
will contain many entries with null properties, because many of the images do not have values defined at the sample point locations. We can filter out those features by filtering for pixels that have non-nullcloud_flag
values (which includes the areas where clouds occur).Next, select the attributes that you want to export, and rename them as needed.
Finally, export the feature collection to create a CSV file.
Here is a link to the complete script: https://code.earthengine.google.com/55b196d9d01154dddcda362eeb90e041