Is It possible to create .median() Surface Refletance image (landSat 8 RS) without clouds before calculating the NDVI in goolge earth engine? I need to work with a year (eg. 2016-01-01, 2016-12-31), so I need to remove the clouds to create a single image free of clouds and then calculate the NDVI.
[GIS] Mask clouds in an ImageCollection google earth engine
google-earth-enginelandsatndvi
Related Solutions
The Surface Reflectance products contain a band called 'cfmask' and 'cfmask_conf'. The documentation tells you that all pixels labelled 4
in the first band are clouds. If you just want to mask clouds from your NDVI calculation you just have to update the image mask. If you want to do an ImageCollection you simply have to .map
it as a function.
An example image from the alps with only clouds masked from the NDVI as well as an NDVI time-series from the region. The great thing about the EarthEngine is that instead of the NDVI mean you can also look at the NDVI time-series for selected areas.
// single image
var lt8 = ee.Image('LANDSAT/LC8_SR/LC81930272016103')
var lt8_ndvi = lt8
.normalizedDifference(['B4', 'B5']) // calculate NDVI
.updateMask(lt8.select(['cfmask']).neq(4)) // mask everything labeled 'cloud'
// Map.centerObject(lt8_ndvi, 10)
Map.addLayer(lt8_ndvi, {}, "LC81930272016103 NDVI cloud masked", 0)
// image collection
var lt8_ic = ee.ImageCollection('LANDSAT/LC8_SR')
.filterBounds(aoi) // filter to area-of-interest
.filterDate(ee.Date("2016-01-01"),ee.Date("2016-12-31")) // filter to 2016
var lt8_ndvi_ic = lt8_ic
.map(function(img){ // add cloud free NDVI to all images
return img.addBands(img.normalizedDifference(['B4', 'B5'])).updateMask(img.select(['cfmask']).neq(4))
});
print(lt8_ndvi_ic)
Map.centerObject(testPoint, 10)
var ndvi_viz = {min:-0.8, max:0.3, palette:'000000,00FF00'};
Map.addLayer(lt8_ndvi_ic.select('nd').mean(), ndvi_viz, "LT8 2016 NDVI mean")
// NDVI time-series chart
// Create and print the chart.
print(ui.Chart.image.series(lt8_ndvi_ic.select('nd'), testPoint, ee.Reducer.mean(), 30));
You can obtain the answer you want with the small geometry you use using a different approach. Note that a Landsat pixel and the scale you are currently using is 30m. Your geometry is just about 10x10m.
Add the moment you are using an unweighted reducer (ee.Reducer.max
), which will only take pixels into account if the centroid is within the geometry (see here). Neither the geometry nor the scale you are using apparently include the centroid of the pixels, thus you return no valid values (which you set to -999 afterwards).
You have two options:
1) Use a scale which will include the centroid and use the unweighted reducer. Set the scale for example at 10m or calculate approximately the scale of a square geometry using:
var scale = table.geometry().area(1).sqrt();
2) Use a weighted reducer which will include pixels which approximately include at least 0.5% of the pixel. An example would be using a mean reducer. In such a small region you probably aggregate one pixel, so that is fine for your max outcome.
var data = cloudlessNDVI.filterDate(ini,end).max().reduceRegion({
reducer: ee.Reducer.mean(),
geometry: feature.geometry(),
scale: 30
});
Here an example returning both values (and you will see they are similar, confirming there is just one pixel in the region).
Best Answer
Based on the answer of Mask clouds in LandSat 8 surface refletance image, one possibility is:
NOTE: replace YOUR_PLACE with a Geometry or Feature