-
Notifications
You must be signed in to change notification settings - Fork 1
/
code.js
261 lines (231 loc) · 8.84 KB
/
code.js
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
// #############################################################################
// ### EXTERNAL PACKAGES ###
// #############################################################################
var txt = require("users/jarickkk/packages:gena-text-dih");
var plt = require("users/jarickkk/packages:gena-palettes");
var dat = require("users/jarickkk/packages:datasets");
var bsm = require("users/jarickkk/packages:basemaps");
var prc = require("users/jarickkk/packages:processing");
// #############################################################################
// ### INPUT PARAMETERS ###
// #############################################################################
// Area of interest
var lakes = ee.FeatureCollection("users/jarickkk/geoportale-veneto/laghi");
var lakeField = "nome";
var lakeName = "LAGO DI FIMON";
var bufDist = 500; // bounding box around aoi, in meters
// Time period (YYYY-MM-dd)
var startDateStr = "2017-05-01"; // s2.dateStart
var endDateStr = "2021-08-30"; // func.currentDateTime().date
// Imagery and indices
var s2 = dat.imgCollections.sent2;
var maxClouds = 40; // percentage
var minNDVI = 0.4; // threshold to separate vegetation
// Map visualization
var mapStyle = "silver"; // ROADMAP, SATELLITE, HYBRID, TERRAIN
// retro, silver, dark, night, aubergine
var visNDVI = {min: 0.0, max: 0.5, palette: plt.cmocean.Algae[7]};
var visNDVI2 = {
bands: "NDVI_median",
min: -0.5, max: 0.9,
palette: plt.cmocean.Speed[7]
};
// palettes: https://github.com/gee-community/ee-palettes#user-content-palettes
var rgbBands = ["RED_median", "GREEN_median", "BLUE_median"];
var visRGB = {bands: rgbBands, min: 192, max: 1106};
var nirBands = ["NIR_median", "RED_median", "GREEN_median"];
var visNIR = {bands: nirBands, min: 60, max: 4000};
var BWBand = ["GREEN"];
var visBW = {bands: BWBand, min: 192, max: 1106, forceRgbOutput: true};
// Map labels
var label = "" + lakeName + " ";
var annot = [
{
position: "top",
offset: "5%",
margin: "2%",
property: "date",
scale: 11, // empiric value - it's bigger for smaller scales
fontType: "Consolas", fontSize: 18, // Consolas 18 or Arial 64
textOpacity: 1.0,
texWidth: 2.0,
outlineWidth: 2.0,
outlineOpacity: 1.0,
},
];
// Export
var crsMap = "EPSG:3857"; // for map output
var crsCalc = "EPSG:32632"; // for area calculation
var scaleS2 = s2.scale; // ground sample distance in meters (for 'scale')
var maxPxCount = 1e13; // max pixel count for export image, exponential form
var fps = 2; // frames per second
var maxDim = 500; //max dimensions of the thumbnail to render, in pixels
//var baseFolderName = "EarthEngine";
//var attributeList = ["field1", "field2", "field3"];
//var projectID = prc.camelize(lakeName);
// #############################################################################
// ### IMPLEMENTATION ###
// #############################################################################
print("Time period:", startDateStr, endDateStr);
// define area of interest
var aoi = lakes.filter(ee.Filter.eq(lakeField, lakeName));
var bboxPrecise = aoi.geometry().bounds();
var bboxWide = bboxPrecise.buffer(bufDist).bounds(); // a bit bigger extent
// prepare image collection
var s2imColF = ee.ImageCollection(s2.path)
.map(prc.s2mask)
.map(prc.renameBands(s2.bandIds, s2.bandNames))
.filter(ee.Filter.lt(s2.cloudAttribute, maxClouds));
var s2imCol = s2imColF // used to know the size of the initial collection
.filterDate(startDateStr, endDateStr)
.filterBounds(bboxWide);
print("S2 collection size:", s2imCol.size());
// add spectral index as a new band
s2imColF = s2imColF.map(prc.addNDVI);
// image collection of monthly median composites
var interval = 1;
var increment = "month";
// make a list of start months
var startDate = ee.Date(startDateStr);
var secondDate = startDate.advance(interval, increment).millis();
var increase = secondDate.subtract(startDate.millis());
var dateList = ee.List.sequence(
startDate.millis(),
ee.Date(endDateStr).millis(),
increase
);
var s2millis = ee.ImageCollection.fromImages(
dateList.map(
function (date) {
return s2imColF.filterDate(
ee.Date(date),
ee.Date(date).advance(interval, increment))
.median()
.set("system:time_start", ee.Date(date).millis()
);
})
);
// create NDVI median composite image
var s2doy = s2millis;
var s2doy = s2doy.map(function (img) {
var doy = ee.Date(img.get("system:time_start")).getRelative("day", "year");
img = img.set({"doy": doy, "date": img.date().format("Y M")});
return img;
});
var s2doyDistinct = s2doy.filterDate(startDateStr, endDateStr);
// define a filter that identifies, which images from the complete collection
// match the DOY from the distinct DOY collection
var doyFilter = ee.Filter.equals({leftField: "doy", rightField: "doy"});
// define a join
var join = ee.Join.saveAll("doy_matches");
// apply the join and convert the resulting FeatureCollection
// to an ImageCollection
var joinCol = ee.ImageCollection(join.apply(s2doyDistinct, s2doy, doyFilter));
// apply median reduction among matching DOY collections
var s2NDVIdate = joinCol.map(function (img) {
var doyCol = ee.ImageCollection.fromImages(img.get("doy_matches"));
return doyCol.reduce(ee.Reducer.median()).set({"date": img.get("date")});
});
print("Size of the monthly composite collection:", s2NDVIdate.size());
// calculate pixel area in hectares
function makeBinary(img) {
var binaryNDVI = img.select("NDVI_median")
.gt(minNDVI)
.rename("NDVI_binary")
.clip(aoi);
binaryNDVI = binaryNDVI.set("date", img.get("date"));
return binaryNDVI;
}
var pxArea = ee.Image.pixelArea().reproject({crs: crsCalc, scale: scaleS2});
function areaMultiply(img) {
var imgBand = img.select("NDVI_binary")
.multiply(pxArea).divide(10000) // convert from meters to hectares
.rename("NDVI_ha");
return imgBand.copyProperties(img, ['date']);
}
function printInfo(imgColl) {
print("S2 Result:", imgColl);
}
var NDVIbinaryArea = s2NDVIdate.map(makeBinary).aside(print).map(areaMultiply).aside(print);
// print("S2 Result:", NDVIbinaryArea.getInfo());
// print("S2 Result:", NDVIbinaryArea.evaluate(fu));
NDVIbinaryArea.evaluate(printInfo);
// #############################################################################
// ### VISUALIZATION ###
// #############################################################################
// ### MAP CANVAS ###
Map.centerObject(aoi, 14);
// add a basemap from the configured value
if (bsm.customList.indexOf(mapStyle) >= 0) {
Map.setOptions("Custom", {Custom: bsm[mapStyle]});
} else if (bsm.googleList.indexOf(mapStyle) >= 0) {
Map.setOptions(mapStyle);
} else {
print("WARNING: Specified basemap not found. Use default map style instead.");
}
var imgVisNDVI = s2imColF.select("NDVI").median().clip(aoi).visualize(visNDVI);
var imgVisBW = s2imColF.select(BWBand).median().clip(bboxWide).visualize(visBW);
// Map layers
//Map.addLayer(compMask, {palette: "blue, green"}, "compMask");
Map.addLayer(s2NDVIdate, visNIR, "NIR (Last)", false);
Map.addLayer(s2NDVIdate, visRGB, "RGB (Last)", true);
Map.addLayer(s2NDVIdate, visNDVI2, "NDVI (Last)", true);
Map.addLayer(NDVIbinaryArea, {}, "Binary NDVI with hectares (Last)", false);
Map.addLayer(imgVisBW, {}, "B/W Vis (Median)", false);
Map.addLayer(imgVisNDVI.clip(aoi), {}, "NDVI Vis (Median)", false);
// ### CHART ###
var chart =
ui.Chart.image
.series({
imageCollection: NDVIbinaryArea,
region: aoi,
reducer: ee.Reducer.sum(),
scale: scaleS2,
xProperty: "date"
})
.setOptions({
title: "Dinamica mensile dell'estensione di vegetazione lacustre/palustre",
chartArea: {backgroundColor: "EBEBEB"},
legend: {position: "none"},
hAxis: {
title: "Data",
titleTextStyle: {italic: false, bold: true},
gridlines: {color: "white"}
},
vAxis: {
title: "Area totale di vegetazione, ha",
titleTextStyle: {italic: false, bold: true},
gridlines: {color: "white"},
baselineColor: "white"
},
lineWidth: 3,
colors: ["green"],
//curveType: "function"
});
print(chart);
// ### ANIMATED TIME SERIES ###
// RGB visualization images to be used as animation frames
function makeMosaic(img) {
var imgBack = img.select(BWBand)
.clip(bboxWide)
.visualize(visBW);
var imgFront = img.select("NDVI")
.clip(aoi)
.visualize(visNDVI)
.set({date: img.get("date")});
var mosaic = ee.ImageCollection([imgBack, imgFront]).mosaic()
.unmask(0)
.set({date: img.get("date")});
return txt.annotateImage(mosaic, {}, bboxWide, annot, label);
}
var rgbVis = s2doy.map(makeMosaic);
// define GIF visualization parameters
var gifParams = {
region: bboxWide,
dimensions: maxDim,
framesPerSecond: fps,
crs: crsMap,
maxPixels: maxPxCount,
};
// print the GIF URL to the console
print("GENERATED GIF ANIMATION:", rgbVis.getVideoThumbURL(gifParams));