-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathutils.js
More file actions
295 lines (243 loc) · 9.52 KB
/
utils.js
File metadata and controls
295 lines (243 loc) · 9.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// |
// | Utility functions for Harmonic Condition Monitoring Workflow
// | [valpasq@bu.edu], 2020
// |
// |
// ---------------------------- LANDSAT Pre-processing ----------------------------
var L457_BANDS = ['B1', 'B2', 'B3', 'B4', 'B5', 'B7', 'B6']; // Landsat TM/ETM+ bands
var L8_BANDS = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B11']; // Landsat OLI bands
var LTS_NAMES = ['blue', 'green', 'red', 'nir', 'swir1', 'swir2', 'temp']; // Common names
var L457_C2_BANDS = ['SR_B1', 'SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B7']; // Landsat TM/ETM+ bands
var L8_C2_BANDS = ['SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B6', 'SR_B7']; // Landsat OLI bands
var LTS_NAMES_SR = ['blue', 'green', 'red', 'nir', 'swir1', 'swir2']; // Common names
var preprocess457 = function(image) {
var mask1 = image.select(['pixel_qa']).eq(66) // Clear land
.or(image.select(['pixel_qa']).eq(68)); // Clear water
var mask2 = image
.select(L457_BANDS)
.mask()
.reduce('min');
var mask3 = image.select(['B1', 'B2', 'B3', 'B4', 'B5', 'B7']).gt(0).and(
image.select(['B1', 'B2', 'B3', 'B4', 'B5', 'B7']).lt(10000))
.reduce('min');
return image.updateMask(mask1.and(mask2).and(mask3))
.multiply(0.0001)
.select(L457_BANDS).rename(LTS_NAMES)
.copyProperties(image, ["system:time_start", "WRS_PATH", "WRS_ROW"])
.set('SENSING_TIME', ee.String(image.get('SENSING_TIME')).split('T').get(0));
};
var preprocess8 = function(image) {
var mask1 = image.select(['pixel_qa']).eq(322) // Clear land
.or(image.select(['pixel_qa']).eq(324)); // Clear water
var mask2 = image
.select(L8_BANDS)
.mask()
.reduce('min');
var mask3 = image.select(['B2', 'B3', 'B4', 'B5', 'B6', 'B7']).gt(0).and(
image.select(['B2', 'B3', 'B4', 'B5', 'B6', 'B7']).lt(10000))
.reduce('min');
return image.updateMask(mask1.and(mask2).and(mask3))
.multiply(0.0001)
.select(L8_BANDS).rename(LTS_NAMES) // Map legacy band names
.copyProperties(image, ["system:time_start", "WRS_PATH", "WRS_ROW"])
.set('SENSING_TIME', ee.String(image.get('SENSING_TIME')).split('T').get(0));
};
var preprocess457_c2 = function(image) {
// Get bits for "bad QA"
var dilatedcloud_bit = 1 << 1;
var cloud_bit = 1 << 3;
var cloudshadow_bit = 1 << 4;
var snow_bit = 1 << 5;
var qa = image.select('QA_PIXEL');
var mask = qa.bitwiseAnd(cloud_bit).eq(0)
.and(qa.bitwiseAnd(dilatedcloud_bit).eq(0))
.and(qa.bitwiseAnd(cloudshadow_bit).eq(0))
.and(qa.bitwiseAnd(snow_bit).eq(0));
var mask2 = image
.select(L457_C2_BANDS)
.mask()
.reduce('min');
return image
.updateMask(mask.and(mask2))
.select(L457_C2_BANDS).rename(LTS_NAMES_SR)
.multiply(0.0000275)
.add(-0.2)
.copyProperties(image, ["system:time_start", "WRS_PATH", "WRS_ROW"])
.set("SENSING_TIME", image.get("DATE_ACQUIRED"));
};
var preprocess8_c2 = function(image) {
// Get bits for "bad QA"
var dilatedcloud_bit = 1 << 1;
var cirrus_bit = 1 << 2;
var cloud_bit = 1 << 3;
var cloudshadow_bit = 1 << 4;
var snow_bit = 1 << 5;
var qa = image.select('QA_PIXEL');
var mask = qa.bitwiseAnd(cloud_bit).eq(0)
.and(qa.bitwiseAnd(dilatedcloud_bit).eq(0))
.and(qa.bitwiseAnd(cirrus_bit).eq(0))
.and(qa.bitwiseAnd(cloudshadow_bit).eq(0))
.and(qa.bitwiseAnd(snow_bit).eq(0));
var mask2 = image
.select(L8_C2_BANDS)
.mask()
.reduce('min');
return image
.updateMask(mask.and(mask2))
.select(L8_C2_BANDS).rename(LTS_NAMES_SR)
.multiply(0.0000275)
.add(-0.2)
.copyProperties(image, ["system:time_start", "WRS_PATH", "WRS_ROW"])
.set("SENSING_TIME", image.get("DATE_ACQUIRED"));
};
// ------------------------- Simple Cloud Score for SR -------------------------
// SOURCE: https://gis.stackexchange.com/questions/280400/
// cloud-cover-percentage-in-google-earth-engine
var cloudScore = function(image) {
// A helper to apply an expression and linearly rescale the output.
var rescale = function(image, exp, thresholds) {
return image.expression(exp, {image: image})
// .divide(10000) // need to divide by 10000 (SR)
.subtract(thresholds[0]).divide(thresholds[1] - thresholds[0]);
};
// Compute several indicators of cloudyness and take the minimum of them.
var score = ee.Image(1.0);
// Clouds are reasonably bright in the blue band.
score = score.min(rescale(image, 'image.blue', [0.1, 0.3]));
// Clouds are reasonably bright in all visible bands.
score = score.min(rescale(image, 'image.red + image.green + image.blue', [0.2, 0.8]));
// Clouds are reasonably bright in all infrared bands.
score = score.min(
rescale(image, 'image.nir + image.swir1 + image.swir2', [0.3, 0.8]));
// Clouds are reasonably cool in temperature.
score = score.min(rescale(image, 'image.temp', [300, 290]));
// However, clouds are not snow.
var ndsi = image.normalizedDifference(['green', 'swir1']);
return score.min(rescale(ndsi, 'image', [0.8, 0.6]));
};
var addCloudScore = function(image) {
// Invert the cloudscore so 1 is least cloudy, and rename the band.
var score = cloudScore(image.select(LTS_NAMES));
score = ee.Image(1).subtract(score).select([0], ['cloudscore']);
return image.addBands(score);
};
var maskCloudScore = function(image) {
var qa = image.select('cloudscore');
var mask = qa.gte(0.8);
return image.updateMask(mask)
.copyProperties(image, ["system:time_start", "WRS_PATH", "WRS_ROW"]);
};
var getNames = function(base, list) {
return ee.List(list).map(function(i) {
return ee.String(base).cat(ee.Number(i).int());
});
};
var addConstant = function(image) {
return image.addBands(ee.Image(1));
};
var addTime = function(image) {
// Compute time in fractional years since the epoch.
var date = ee.Date(image.get('system:time_start'));
var years = date.difference(ee.Date('1970-01-01'), 'year');
var timeRadians = ee.Image(years.multiply(2 * Math.PI));
return image.addBands(timeRadians.rename('t').float());
};
var spectralTransformsFnFactory = function(dependent) {
return function(img){
// var scaled = img.divide(10000);
var dict = {
blue: img.select("blue"),
green: img.select("green"),
red: img.select("red"),
nir: img.select("nir"),
swir1: img.select("swir1"),
swir2: img.select("swir2"),
};
var indexImg;
switch (dependent.toUpperCase()){
case 'NBR':
indexImg = img.normalizedDifference(['nir', 'swir2'])
.rename("nbr");
break;
case 'NDMI':
indexImg = img.normalizedDifference(['nir', 'swir1'])
.rename("ndmi");
break;
case 'NDVI':
indexImg = img.normalizedDifference(['nir', 'red'])
.rename("ndvi");
break;
case 'NDSI':
indexImg = img.normalizedDifference(['green', 'swir1'])
.rename("ndsi");
break;
case 'EVI':
indexImg = img.expression("2.5 * ((nir - red) / (nir + 6 * red - 7.5 * blue + 1))", dict)
.rename("evi");
break;
case 'TCB':
indexImg = img.expression("0.2043*blue + 0.4158*green + 0.5524*red + 0.5741*nir + 0.3124*swir1 + 0.2303*swir2", dict)
.rename("tcb");
break;
case 'TCG':
indexImg = img.expression("-0.1603*blue - 0.2819*green - 0.4934*red + 0.7940*nir - 0.0002*swir1 - 0.1446*swir2", dict)
.rename("tcg");
break;
case 'TCW':
indexImg = img.expression("0.0315*blue + 0.2021*green + 0.3102*red + 0.1594*nir - 0.6806*swir1 - 0.6109*swir2", dict)
.rename("tcw");
break;
case 'SR':
indexImg = img.select('nir').divide(scaled.select('red'))
.rename("sr");
break;
default:
print('The index you provided is not supported');
}
return indexImg
.toFloat()
.copyProperties(img, ["system:time_start", "WRS_PATH", "WRS_ROW", "SENSING_TIME"]);
};
};
var addHarmonicsFnFactory = function(freqs, cosNames, sinNames) {
return function(image) {
// Make an image of frequencies.
var frequencies = ee.Image.constant(freqs);
// This band should represent time in radians.
var time = ee.Image(image).select('t');
// Get the cosine terms.
var cosines = time.multiply(frequencies).cos().rename(cosNames);
// Get the sin terms.
var sines = time.multiply(frequencies).sin().rename(sinNames);
return image.addBands(cosines).addBands(sines);
};
};
var addPredictionFnFactory = function(independents, model) {
return function(image) {
var prediction = image.select(independents)
.multiply(model.select(independents))
.reduce(ee.Reducer.sum().forEachElement())
.rename('prediction');
var rmse = model.select('rmse').rename('rmse');
var nobs = model.select('nobs').rename('nobs');
var t = model.select('t').rename('trend');
return image.addBands(prediction)
.addBands(rmse).addBands(nobs).addBands(t);
};
};
// -------------------------------- Export --------------------------------
exports = {
getNames: getNames,
addConstant: addConstant,
addTime: addTime,
addHarmonicsFnFactory: addHarmonicsFnFactory,
preprocess457: preprocess457,
preprocess457_c2: preprocess457_c2,
preprocess8: preprocess8,
preprocess8_c2: preprocess8_c2,
cloudScore: cloudScore,
addCloudScore: addCloudScore,
maskCloudScore: maskCloudScore,
spectralTransformsFnFactory: spectralTransformsFnFactory,
addPredictionFnFactory: addPredictionFnFactory
}