1 |
|
// MCD09_MCloudFreq
|
2 |
|
////////////////////////////////////////////////////////////
|
|
1 |
// MCD09CF
|
|
2 |
///////////////////////////////////////////////////////////
|
3 |
3 |
// Adam.wilson@yale.edu
|
4 |
|
// Generate a cloud frequency climatology from MOD09 data
|
|
4 |
// Generate a cloud frequency climatology from M*D09 data
|
5 |
5 |
// This script does the following
|
6 |
6 |
// 1) Extracts daily cloud flag from MOD09GA and MYD09GA and create single collection for each month
|
7 |
7 |
// 2) Calculate mean cloud frequency for each month
|
... | ... | |
10 |
10 |
////////////////////////////////////////////////////////////
|
11 |
11 |
|
12 |
12 |
// Specify destination and run name
|
13 |
|
var driveFolder="ee_combined";
|
14 |
|
var run="combined"
|
|
13 |
var driveFolder="ee_mcd09cf";
|
|
14 |
var run="g2"
|
|
15 |
|
|
16 |
// limit overall date range (only dates in this range will be included)
|
|
17 |
var datestart=new Date("2000-01-01") // default time zone is UTC
|
|
18 |
var datestop=new Date("2014-02-28")
|
15 |
19 |
|
16 |
|
// limit overall date range by year (only years in this range will be included)
|
17 |
|
var yearstart=2000
|
18 |
|
var yearstop=2013
|
19 |
20 |
|
20 |
|
// limit overall date range by month
|
|
21 |
// specify which months (within the date range above) to process
|
21 |
22 |
var monthstart=1
|
22 |
23 |
var monthstop=12
|
23 |
24 |
|
24 |
25 |
// specify what happens
|
25 |
|
var verbose=true // print info about collections along the way (slows things down)
|
|
26 |
var verbose=false // print info about collections along the way (slows things down)
|
26 |
27 |
var exportDrive=true // add exports to task window
|
27 |
|
var exportGME=false // add exports to task window and send to GME
|
28 |
|
var download=false // show download URL
|
29 |
|
var drawmap=false // add image to map
|
|
28 |
var drawmap=false // add image to map
|
|
29 |
|
|
30 |
|
|
31 |
// define regions
|
|
32 |
var globe = '[[-180, -89.95], [-180, 89.95], [180, 89.95], [180, -89.95]]';
|
|
33 |
var sahara = '[[-18, 0], [-18, 30], [15, 30], [15, 0]]';
|
|
34 |
// choose region to export (if exportDrive==true)
|
|
35 |
var region = globe
|
30 |
36 |
|
31 |
37 |
|
|
38 |
/////////////////////////////////////////////////////////////////////////////
|
|
39 |
/////////////////////////////////////////////////////////////////////////////
|
|
40 |
/////////////////////////////////////////////////////////////////////////////
|
|
41 |
// Generate some filtering variables based on input above and date
|
|
42 |
|
32 |
43 |
// Get current date as string for file metadata
|
33 |
|
var currentdate = new Date()
|
34 |
|
var date= currentdate.getFullYear()+''+currentdate.getMonth()+1+''+currentdate.getDate()
|
|
44 |
var currentdate = new Date();
|
|
45 |
var date= currentdate.getFullYear()+''+("0" + (currentdate.getMonth() + 1)).slice(-2)+''+currentdate.getDate();
|
|
46 |
print(date)
|
|
47 |
// identify start and stop years
|
|
48 |
var yearstart=datestart.getUTCFullYear();
|
|
49 |
var yearstop=datestop.getUTCFullYear();
|
35 |
50 |
|
36 |
51 |
// get array of years to process
|
37 |
52 |
var years=Array.apply(0, Array(yearstop-yearstart+1)).map(function (x, y) { return yearstart +y ; });
|
38 |
|
var nYears=years.length
|
|
53 |
var nYears=years.length;
|
39 |
54 |
if(verbose){print('Processing '+years)}
|
40 |
55 |
|
41 |
56 |
/////////////////////////////////////////////////////////////////////////////
|
42 |
57 |
/// Functions
|
|
58 |
|
|
59 |
//function to combine terra and aqua and limit by date
|
|
60 |
function getMCD09(modcol,datestart,datestop){
|
|
61 |
var getdates=ee.Filter.date(datestart,datestop);
|
|
62 |
return(ee.ImageCollection(modcol).filter(getdates));
|
|
63 |
}
|
|
64 |
|
|
65 |
//function to subset a collection by year and month
|
|
66 |
function fyearmonth(collection,year,month){
|
|
67 |
// define filters
|
|
68 |
var getyear=ee.Filter.calendarRange(year,year,"year");
|
|
69 |
var getmonth=ee.Filter.calendarRange(month,month,"month");
|
|
70 |
// extract values
|
|
71 |
var output=collection.filter(getmonth).filter(getyear);
|
|
72 |
return(output);
|
|
73 |
}
|
|
74 |
|
|
75 |
///////////////////////////////////////////////////////////////////////////
|
|
76 |
// Loop through months and get cloud frequency for each month in year range
|
|
77 |
|
|
78 |
var monthmean=function(collection,month){
|
|
79 |
//For this month, build array to hold means for every year
|
|
80 |
var mod = new Array (nYears);
|
|
81 |
// loop over years and and put the mean monthly CF in the array
|
|
82 |
for (var i=0; i<nYears; i ++) {
|
|
83 |
mod[i]= fyearmonth(collection,years[i],month). // filter by year-month
|
|
84 |
map(getcf). // extract cloud frequency
|
|
85 |
mean(); // take the mean
|
|
86 |
}
|
|
87 |
if(verbose){print('Processing '+nYears+' years of data for Month '+month)}
|
|
88 |
// build an image collection for all years for this month using the array
|
|
89 |
return(ee.ImageCollection(mod));
|
|
90 |
}
|
43 |
91 |
/**
|
44 |
92 |
* Returns an image containing just the specified QA bits.
|
45 |
93 |
*
|
... | ... | |
51 |
99 |
*/
|
52 |
100 |
var getQABits = function(image, start, end, newName) {
|
53 |
101 |
// Compute the bits we need to extract.
|
54 |
|
var pattern = 0;
|
55 |
|
for (var i = start; i <= end; i++) {
|
56 |
|
pattern += Math.pow(2, i);
|
|
102 |
var i, pattern = 0;
|
|
103 |
for (i = start; i <= end; i++) {
|
|
104 |
pattern += Math.pow(2, i);
|
57 |
105 |
}
|
58 |
106 |
return image.select([0], [newName])
|
59 |
|
.bitwise_and(pattern)
|
60 |
|
.right_shift(start);
|
|
107 |
.bitwise_and(pattern)
|
|
108 |
.right_shift(start);
|
61 |
109 |
};
|
62 |
110 |
|
63 |
|
// function to extract MOD09 internal cloud flag
|
64 |
|
//var getcf=function(img){ return(img.select(['state_1km']).expression("((b(0)/1024)%1)").multiply(ee.Image(100)))};
|
65 |
|
var getcf=function(img){ return( getQABits(img.select(['state_1km']),10, 10, 'cloud').expression("b(0) == 1").multiply(ee.Image(100)))};
|
66 |
111 |
|
67 |
|
//function to get mean combined (terra + aqua) cloud frequency for a particular year-month
|
68 |
|
function fMOD09(year,month,collection){
|
69 |
|
// define filters
|
70 |
|
var getmonth=ee.Filter.calendarRange(month,month,"month");
|
71 |
|
var getyear=ee.Filter.calendarRange(year,year,"year");
|
72 |
|
// extract values
|
73 |
|
var MOD09=ee.ImageCollection(collection).filter(getmonth).filter(getyear).map(getcf).mean();
|
74 |
|
// get monthly combined mean
|
75 |
|
var tMOD09m=MOD09;
|
76 |
|
return(tMOD09m);
|
77 |
|
}
|
78 |
112 |
|
|
113 |
// function to extract MOD09 internal cloud flag
|
|
114 |
var getcf=function(img){
|
|
115 |
var img2=getQABits(img.select(['state_1km']),10, 10, 'cloud'); // extract cloud flag from bit 10
|
|
116 |
return(img2.
|
|
117 |
mask(img2.gte(0)). // mask out NAs
|
|
118 |
gte(1). // Convert to logical
|
|
119 |
multiply(ee.Image(10000)))}; // Multiply by 10000 to facilate taking mean as an integer 0-10000
|
|
120 |
|
79 |
121 |
// function to return the total number of observations in the collection
|
80 |
122 |
var getcount=function(img) {
|
81 |
|
return img.select(['num_observations_1km']).select([0],['nObs'])}//.multiply(ee.Image(10))};
|
|
123 |
var img2=img.select(['num_observations_1km']);
|
|
124 |
return img2.
|
|
125 |
mask(img2.gte(0)). // mask out NAs
|
|
126 |
select([0],['nObs']). // rename to nObs
|
|
127 |
multiply(ee.Image(10000))}; // multiply to 100 to facilate taking mean as an integer
|
82 |
128 |
|
83 |
129 |
// function to return the proportion of days with at least one observation in the collection
|
84 |
130 |
var getprop=function(img) {
|
85 |
|
return img.select(['num_observations_1km']).gte(1).multiply(ee.Image(100)).select([0],['pObs'])};
|
86 |
|
|
|
131 |
var img2=img.select(['num_observations_1km']);
|
|
132 |
return img2.
|
|
133 |
mask(img2.gte(0)). // mask out NAs
|
|
134 |
gte(1). // Convert to logical to get percentage of days with n>0
|
|
135 |
multiply(ee.Image(10000)). // multiply to 100 to facilate taking mean as an integer
|
|
136 |
select([0],['pObs'])}; // rename to nObs
|
87 |
137 |
|
88 |
|
////////////////////////////////////////////////////
|
89 |
|
// Loop through months and get cloud frequency for each month in year range
|
90 |
|
for (var m = monthstart; m <= monthstop; m ++ ) {
|
91 |
138 |
|
92 |
|
//For this month, build array to hold means for every year
|
93 |
|
var mod = new Array (nYears);
|
94 |
|
var myd = new Array (nYears);
|
95 |
|
|
96 |
|
// loop over years and and put the mean monthly CF in the array
|
97 |
|
for (var i=0; i<nYears; i ++) {
|
98 |
|
mod[i]= fMOD09(years[i],m,"MOD09GA");
|
99 |
|
myd[i]= fMOD09(years[i],m,"MYD09GA");
|
100 |
|
}
|
|
139 |
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
140 |
// Clip high-latitude nonsense
|
|
141 |
// For an unknown reason there are pixels at high latitudes with nobs>0 in winter (dark) months.
|
|
142 |
// These values were identified by viewing the monthly CF and nObs datasets and identifying the latitude
|
|
143 |
// at which there was a stripe of fully missing data (nObs=0) followed by an erroneous region at higher latitudes
|
|
144 |
// This returns a polygon for any month which can be used to clip the image
|
|
145 |
var monthbox=function(month) {
|
|
146 |
// limits for each month (12 numbers correspond to jan-december limits)
|
|
147 |
var ymin=[-90,-90,-90,-90,-70,-70,-70,-77,-77,-90,-90,-90];
|
|
148 |
var ymax=[ 77, 90, 90, 90, 90, 90, 90, 90, 90, 90, 77, 69];
|
|
149 |
// draw the polygon, use month-1 because month is 1:12 and array is indexed 0:11
|
|
150 |
var ind=month-1;
|
|
151 |
var box = ee.Geometry.Rectangle(-180,ymin[ind],180,ymax[ind]);
|
|
152 |
return (box)};
|
101 |
153 |
|
102 |
|
if(verbose){print('Processing '+nYears+' years of data for Month '+m)}
|
103 |
154 |
|
104 |
|
// build an image collection for all years for this month using the array
|
105 |
|
var MOD09m=ee.ImageCollection(mod);
|
106 |
|
var MYD09m=ee.ImageCollection(myd);
|
|
155 |
////////////////////////////////////////////////////
|
|
156 |
////////////////////////////////////////////////////
|
|
157 |
////////////////////////////////////////////////////
|
|
158 |
////////////////////////////////////////////////////
|
|
159 |
// Start processing the data
|
107 |
160 |
|
108 |
|
if(verbose){print(MOD09m.getInfo())}
|
|
161 |
//var mcol='MOD09GA'
|
|
162 |
//var tmonth=1
|
109 |
163 |
|
110 |
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
111 |
|
/// Process MODIS observation frequency/proportion
|
112 |
164 |
|
113 |
|
// get number of obs and proportion of days with at least one obs
|
114 |
|
//MOD
|
115 |
|
var MOD09_pObs = ee.ImageCollection("MOD09GA").
|
116 |
|
filter(ee.Filter.calendarRange(yearstart,yearstop,"year")).
|
117 |
|
filter(ee.Filter.calendarRange(m,m,"month")).map(getprop).mean().byte();
|
|
165 |
// loop over collections and months
|
|
166 |
var mcols = ['MOD09GA','MYD09GA'];
|
|
167 |
for (var c=0;c<mcols.length;c++) {
|
|
168 |
var mcol=mcols[c];
|
|
169 |
for (var tmonth = monthstart; tmonth <= monthstop; tmonth ++ ) {
|
118 |
170 |
|
119 |
|
var MOD09_nObs = ee.ImageCollection("MOD09GA").
|
120 |
|
filter(ee.Filter.calendarRange(yearstart,yearstop,"year")).
|
121 |
|
filter(ee.Filter.calendarRange(m,m,"month")).map(getcount).mean().multiply(ee.Image(4)).byte();
|
|
171 |
if(verbose){
|
|
172 |
print('Starting processing for:'+ mcol+' month '+tmonth);
|
|
173 |
}
|
122 |
174 |
|
123 |
|
//MYD
|
124 |
|
var MYD09_pObs = ee.ImageCollection("MYD09GA").
|
125 |
|
filter(ee.Filter.calendarRange(yearstart,yearstop,"year")).
|
126 |
|
filter(ee.Filter.calendarRange(m,m,"month")).map(getprop).mean().byte();
|
127 |
175 |
|
128 |
|
var MYD09_nObs = ee.ImageCollection("MYD09GA").
|
129 |
|
filter(ee.Filter.calendarRange(yearstart,yearstop,"year")).
|
130 |
|
filter(ee.Filter.calendarRange(m,m,"month")).map(getcount).mean().multiply(ee.Image(4)).byte();
|
|
176 |
// build a combined M*D09GA collection limited by start and stop dates
|
|
177 |
var MCD09all=getMCD09(mcol,datestart,datestop);
|
131 |
178 |
|
132 |
179 |
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
133 |
180 |
// Process cloud frequency
|
134 |
181 |
|
135 |
|
// take overall mean and SD across all years for this month for MOD
|
136 |
|
var MOD09_mean=MOD09m.mean().byte();
|
137 |
|
var MOD09_sd=MOD09m.reduce(ee.call("Reducer.sampleStdDev")).multiply(ee.Image(4)).byte();
|
138 |
|
|
139 |
|
|
140 |
|
// take overall mean and SD across all years for this month for MYD
|
141 |
|
var MYD09_mean=MYD09m.mean().byte();
|
142 |
|
var MYD09_sd=MYD09m.reduce(ee.call("Reducer.sampleStdDev")).multiply(ee.Image(4)).byte();
|
|
182 |
var MCD09m=monthmean(MCD09all,tmonth);
|
|
183 |
if(verbose){
|
|
184 |
print('MCD09m info:');
|
|
185 |
print(MCD09m.getInfo());
|
|
186 |
}
|
143 |
187 |
|
|
188 |
// take overall mean and SD across all years for this month
|
|
189 |
var MCD09_mean=MCD09m.mean();
|
|
190 |
var MCD09_sd=MCD09m.reduce(ee.call("Reducer.sampleStdDev"));
|
144 |
191 |
|
145 |
192 |
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
146 |
|
// Build a single 8-bit image with all bands
|
147 |
|
var MOD09=MOD09_mean.addBands(MOD09_sd).addBands(MOD09_nObs).addBands(MOD09_pObs);
|
148 |
|
var MOD09o=MOD09_nObs.addBands(MOD09_pObs); // Strange, include this line or the above line doesn't work
|
149 |
|
var MOD09o2=MOD09_pObs.addBands(MOD09_nObs); // Strange, include this line or the above line doesn't work
|
150 |
|
|
151 |
|
var MYD09=MYD09_mean.addBands(MYD09_sd).addBands(MYD09_nObs).addBands(MYD09_pObs);
|
152 |
|
var MYD09o=MYD09_nObs.addBands(MYD09_pObs); // Strange, include this line or the above line doesn't work
|
153 |
|
var MYD09o2=MYD09_pObs.addBands(MYD09_nObs); // Strange, include this line or the above line doesn't work
|
154 |
|
|
155 |
|
|
156 |
|
if(verbose){ print(MOD09.getInfo()) }
|
157 |
|
//if(verbose){ print(MOD09o.getInfo()) }
|
158 |
|
|
159 |
|
|
160 |
|
// Test with getDownloadURL
|
161 |
|
if(download){
|
162 |
|
var path = MOD09.getDownloadURL({
|
163 |
|
'name': date+'_'+run+'_MOD09_'+m,
|
164 |
|
'crs': 'SR-ORG:6974', //4326
|
165 |
|
'scale': '926.625433055833',
|
166 |
|
'region': '[[-18, 0], [-18, 30], [15, 30], [15, 0]]' // Sahara
|
167 |
|
});
|
168 |
|
print(path);
|
169 |
|
}
|
170 |
|
|
171 |
|
|
172 |
|
if(exportDrive){
|
173 |
|
exportImage(MOD09,
|
174 |
|
date+'_'+run+'_'+yearstart+yearstop+'_MOD09_'+m,
|
175 |
|
{'maxPixels':1000000000,
|
176 |
|
'driveFolder':driveFolder,
|
177 |
|
// 'crs': 'EPSG:4326', //4326
|
178 |
|
'crs': 'SR-ORG:6974', //4326
|
179 |
|
'scale': '926.625433055833',
|
180 |
|
// 'crsTransform':[0.008333333333, 0, -180, 0, -0.008333333333, -90],
|
181 |
|
'region': '[[-180, -89.9], [-180, 89.9], [180, 89.9], [180, -89.9]]' //global
|
182 |
|
// 'region': '[[-18, 0], [-18, 30], [15, 30], [15, 0]]' // Sahara
|
183 |
|
});
|
184 |
|
|
185 |
|
exportImage(MYD09,
|
186 |
|
date+'_'+run+'_'+yearstart+yearstop+'_MYD09_'+m,
|
187 |
|
{'maxPixels':1000000000,
|
188 |
|
'driveFolder':driveFolder,
|
189 |
|
// 'crs': 'EPSG:4326', //4326
|
190 |
|
'crs': 'SR-ORG:6974', //4326
|
191 |
|
'scale': '926.625433055833',
|
192 |
|
// 'crsTransform':[0.008333333333, 0, -180, 0, -0.008333333333, -90],
|
193 |
|
'region': '[[-180, -89.9], [-180, 89.9], [180, 89.9], [180, -89.9]]' //global
|
194 |
|
// 'region': '[[-18, 0], [-18, 30], [15, 30], [15, 0]]' // Sahara
|
195 |
|
});
|
196 |
|
}
|
197 |
|
|
198 |
|
|
199 |
|
// Export to GME
|
200 |
|
if(exportGME){
|
201 |
|
exportImage(MCD09,
|
202 |
|
date+'_GME_'+run+'_MCD09_'+m,
|
203 |
|
{'maxPixels':1000000000,
|
204 |
|
// 'crs': 'EPSG:4326', //4326
|
205 |
|
'crs': 'SR-ORG:6974', //4326
|
206 |
|
'scale': '926.625433055833',
|
207 |
|
// 'crsTransform':[0.008333333333, 0, -180, 0, -0.008333333333, -90],
|
208 |
|
// 'region': '[[-180, -89], [-180, 89], [180, 89], [180, -89]]' //global
|
209 |
|
'region': '[[-18, 0], [-18, 30], [15, 30], [15, 0]]', // Sahara
|
210 |
|
'gmeProjectId' : 'MAP OF LIFE',
|
211 |
|
'gmeAttributionName': 'Copyright MAP OF LIFE',
|
212 |
|
'gmeMosaic': 'cloud'
|
213 |
|
});
|
214 |
|
}
|
|
193 |
/// Process MODIS observation frequency/proportion
|
215 |
194 |
|
|
195 |
// get number of obs and proportion of days with at least one obs
|
|
196 |
var MCD09_pObs = MCD09all.filter(ee.Filter.calendarRange(tmonth,tmonth,"month")).map(getprop).mean();
|
|
197 |
var MCD09_nObs = MCD09all.filter(ee.Filter.calendarRange(tmonth,tmonth,"month")).map(getcount).mean();
|
216 |
198 |
|
217 |
|
} // close loop through months
|
|
199 |
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
200 |
// Process monthly cloud trends
|
|
201 |
//var trend= MCD09m.formaTrend(null,2)
|
|
202 |
//if(verbose) print(trend.getInfo())
|
218 |
203 |
|
|
204 |
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
205 |
// Build a single 8-bit image with all bands
|
|
206 |
// mask by nobs (only keep pixels where nobs > 0.75)
|
|
207 |
var MCD09=MCD09_mean.
|
|
208 |
addBands(MCD09_sd).
|
|
209 |
addBands(MCD09_nObs).
|
|
210 |
addBands(MCD09_pObs).
|
|
211 |
mask(MCD09_nObs.gte(25)).
|
|
212 |
clip(monthbox(tmonth)).
|
|
213 |
int16();
|
|
214 |
|
|
215 |
if(exportDrive){
|
|
216 |
//define export filename
|
|
217 |
var filename=date+'_'+run+'_'+yearstart+yearstop+'_'+mcol+'_'+tmonth;
|
|
218 |
if(verbose){ print('Exporting to: '+filename)}
|
|
219 |
|
|
220 |
exportImage(MCD09,filename,
|
|
221 |
{'maxPixels':1000000000,
|
|
222 |
'driveFolder':driveFolder,
|
|
223 |
'crs': 'SR-ORG:6974', //4326
|
|
224 |
'scale': '926.625433055833',
|
|
225 |
'region': region
|
|
226 |
});
|
|
227 |
}
|
|
228 |
|
|
229 |
} // close loop through months
|
|
230 |
} // close loop through collections
|
219 |
231 |
|
220 |
232 |
// Draw the map?
|
221 |
233 |
if(drawmap) {
|
222 |
|
var palette="000000,00FF00,FF0000"
|
223 |
|
|
224 |
|
//addToMap(MOD09,{min:0,max:100,palette:palette}, "mod09");
|
225 |
|
addToMap(MOD09_mean,{min:0,max:100,palette:palette}, "mean");
|
226 |
|
addToMap(MOD09_sd,{min:0,max:200,palette:palette}, "sd");
|
227 |
|
addToMap(MOD09_nObs,{min:0,max:20,palette:palette}, "nObs");
|
228 |
|
addToMap(MOD09_pObs,{min:0,max:100,palette:palette}, "pObs");
|
229 |
|
|
230 |
|
}
|
|
234 |
// var palette="000000,00FF00,FF0000";
|
|
235 |
var palette="000000,FFFFFF";
|
|
236 |
|
|
237 |
addToMap(MCD09.select([0]),{min:0,max:10000,palette:palette}, "mean");
|
|
238 |
addToMap(MCD09.select([1]),{min:0,max:5000,palette:palette}, "sd");
|
|
239 |
addToMap(MCD09.select([2]),{min:0,max:15000,palette:palette}, "nObs");
|
|
240 |
addToMap(MCD09.select([3]),{min:0,max:10000,palette:palette}, "pObs");
|
|
241 |
// addToMap(trend.select('long-trend'), {min:-10, max:10, palette:palette}, 'trend');
|
|
242 |
}
|
converted cloud processing script to 16-bit to avoid rounding errors in stripe correction, re-ran global run g2, and started correction processing