openforis-whisp 2.0.0b1__py3-none-any.whl → 2.0.0b3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,8 +7,10 @@
7
7
  # b) a prefix of "nXX_" if it is national/sub-national dataset (where XX is replaced by that country code), or a prefix of 'g_' if it covers more than one country.
8
8
  # c) a name for your image, defined by ".rename('add_your_image_name_here')". This becomes the column header in the output table.
9
9
 
10
- # NB for all the above you will need to be running the package in editable mode for these local changes to take effect.
11
- # Editable mode runs the package locally and thus changes to any files are reflected immediately.
10
+ # Tips:
11
+ # -Avoid getInfo() and for loops to speed up processing by keeping everything in the Earth Engine API.
12
+ # -For all the above you will need to be running the package in editable mode for these local changes to take effect.
13
+ # Editable mode runs the package locally and thus changes to any files are reflected immediately.
12
14
 
13
15
  import ee
14
16
 
@@ -24,6 +26,11 @@ from datetime import datetime
24
26
  # defining here instead of importing from config_runtime, to allow functioning as more of a standalone script
25
27
  geometry_area_column = "Area"
26
28
 
29
+ # Calculate current year once at module load time (not in functions)
30
+ # This avoids repeated datetime calls and potential .getInfo() calls
31
+ CURRENT_YEAR = datetime.now().year
32
+ CURRENT_YEAR_2DIGIT = CURRENT_YEAR % 100 # Last two digits for RADD datasets
33
+
27
34
  import inspect
28
35
 
29
36
  import logging
@@ -49,13 +56,13 @@ def g_esa_worldcover_trees_prep():
49
56
  esa_worldcover_trees_2020 = esa_worldcover_2020_raw.eq(95).Or(
50
57
  esa_worldcover_2020_raw.eq(10)
51
58
  ) # get trees and mnangroves
52
- return esa_worldcover_trees_2020.rename("ESA_TC_2020")
59
+ return esa_worldcover_trees_2020.rename("ESA_TC_2020").selfMask()
53
60
 
54
61
 
55
62
  # EUFO_2020
56
63
  def g_jrc_gfc_2020_prep():
57
64
  jrc_gfc2020_raw = ee.ImageCollection("JRC/GFC2020/V2")
58
- return jrc_gfc2020_raw.mosaic().rename("EUFO_2020")
65
+ return jrc_gfc2020_raw.mosaic().rename("EUFO_2020").selfMask()
59
66
 
60
67
 
61
68
  # GFC_TC_2020
@@ -64,7 +71,7 @@ def g_glad_gfc_10pc_prep():
64
71
  gfc_treecover2000 = gfc.select(["treecover2000"])
65
72
  gfc_loss2001_2020 = gfc.select(["lossyear"]).lte(20)
66
73
  gfc_treecover2020 = gfc_treecover2000.where(gfc_loss2001_2020.eq(1), 0)
67
- return gfc_treecover2020.gt(10).rename("GFC_TC_2020")
74
+ return gfc_treecover2020.gt(10).rename("GFC_TC_2020").selfMask()
68
75
 
69
76
 
70
77
  # GLAD_Primary
@@ -77,8 +84,10 @@ def g_glad_pht_prep():
77
84
  )
78
85
  gfc = ee.Image("UMD/hansen/global_forest_change_2024_v1_12")
79
86
  gfc_loss2001_2020 = gfc.select(["lossyear"]).lte(20)
80
- return primary_ht_forests2001.where(gfc_loss2001_2020.eq(1), 0).rename(
81
- "GLAD_Primary"
87
+ return (
88
+ primary_ht_forests2001.where(gfc_loss2001_2020.eq(1), 0)
89
+ .rename("GLAD_Primary")
90
+ .selfMask()
82
91
  )
83
92
 
84
93
 
@@ -90,7 +99,7 @@ def g_jrc_tmf_undisturbed_prep():
90
99
  .mosaic()
91
100
  .eq(1)
92
101
  ) # update from https://github.com/forestdatapartnership/whisp/issues/42
93
- return TMF_undist_2020.rename("TMF_undist")
102
+ return TMF_undist_2020.rename("TMF_undist").selfMask()
94
103
 
95
104
 
96
105
  # Forest Persistence FDaP
@@ -99,7 +108,7 @@ def g_fdap_forest_prep():
99
108
  "projects/forestdatapartnership/assets/community_forests/ForestPersistence_2020"
100
109
  )
101
110
  fdap_forest = fdap_forest_raw.gt(0.75)
102
- return fdap_forest.rename("Forest_FDaP")
111
+ return fdap_forest.rename("Forest_FDaP").selfMask()
103
112
 
104
113
 
105
114
  #########################primary forest
@@ -107,27 +116,27 @@ def g_fdap_forest_prep():
107
116
  def g_gft_primary_prep():
108
117
  gft_raw = ee.ImageCollection("JRC/GFC2020_subtypes/V0").mosaic()
109
118
  gft_primary = gft_raw.eq(10)
110
- return gft_primary.rename("GFT_primary")
119
+ return gft_primary.rename("GFT_primary").selfMask()
111
120
 
112
121
 
113
122
  # Intact Forest Landscape 2020
114
123
  def g_ifl_2020_prep():
115
124
  IFL_2020 = ee.Image("users/potapovpeter/IFL_2020")
116
- return IFL_2020.rename("IFL_2020")
125
+ return IFL_2020.rename("IFL_2020").selfMask()
117
126
 
118
127
 
119
128
  # European Primary Forest Dataset
120
129
  def g_epfd_prep():
121
130
  EPFD = ee.FeatureCollection("HU_BERLIN/EPFD/V2/polygons")
122
131
  EPFD_binary = ee.Image().paint(EPFD, 1)
123
- return EPFD_binary.rename("European_Primary_Forest")
132
+ return EPFD_binary.rename("European_Primary_Forest").selfMask()
124
133
 
125
134
 
126
135
  # EUFO JRC Global forest type - naturally regenerating planted/plantation forests
127
136
  def g_gft_nat_reg_prep():
128
137
  gft_raw = ee.ImageCollection("JRC/GFC2020_subtypes/V0").mosaic()
129
138
  gft_nat_reg = gft_raw.eq(1)
130
- return gft_nat_reg.rename("GFT_naturally_regenerating")
139
+ return gft_nat_reg.rename("GFT_naturally_regenerating").selfMask()
131
140
 
132
141
 
133
142
  #########################planted and plantation forests
@@ -136,13 +145,13 @@ def g_gft_nat_reg_prep():
136
145
  def g_gft_plantation_prep():
137
146
  gft_raw = ee.ImageCollection("JRC/GFC2020_subtypes/V0").mosaic()
138
147
  gft_plantation = gft_raw.eq(20)
139
- return gft_plantation.rename("GFT_planted_plantation")
148
+ return gft_plantation.rename("GFT_planted_plantation").selfMask()
140
149
 
141
150
 
142
151
  def g_iiasa_planted_prep():
143
152
  iiasa = ee.Image("projects/sat-io/open-datasets/GFM/FML_v3-2")
144
153
  iiasa_PL = iiasa.eq(31).Or(iiasa.eq(32))
145
- return iiasa_PL.rename("IIASA_planted_plantation")
154
+ return iiasa_PL.rename("IIASA_planted_plantation").selfMask()
146
155
 
147
156
 
148
157
  #########################TMF regrowth in 2023
@@ -151,7 +160,7 @@ def g_tmf_regrowth_prep():
151
160
  TMF_AC = ee.ImageCollection("projects/JRC/TMF/v1_2024/AnnualChanges").mosaic()
152
161
  TMF_AC_2023 = TMF_AC.select("Dec2023")
153
162
  Regrowth_TMF = TMF_AC_2023.eq(4)
154
- return Regrowth_TMF.rename("TMF_regrowth_2023")
163
+ return Regrowth_TMF.rename("TMF_regrowth_2023").selfMask()
155
164
 
156
165
 
157
166
  ############tree crops
@@ -181,20 +190,22 @@ def g_creaf_descals_palm_prep():
181
190
  )
182
191
  .mosaic()
183
192
  .select("minNBR_date")
184
- )
193
+ ).selfMask()
185
194
 
186
195
  # Calculate the year of plantation and select all below and including 2020
187
196
  oil_palm_plantation_year = img.divide(365).add(1970).floor().lte(2020)
188
197
 
189
198
  # Create a mask for plantations in the year 2020 or earlier
190
- plantation_2020 = oil_palm_plantation_year.lte(2020).selfMask()
191
- return plantation_2020.rename("Oil_palm_Descals")
199
+ plantation_2020 = oil_palm_plantation_year.lte(2020)
200
+ return plantation_2020.rename("Oil_palm_Descals").selfMask()
192
201
 
193
202
 
194
203
  # Cocoa_ETH
195
204
  def g_eth_kalischek_cocoa_prep():
196
- return ee.Image("projects/ee-nk-cocoa/assets/cocoa_map_threshold_065").rename(
197
- "Cocoa_ETH"
205
+ return (
206
+ ee.Image("projects/ee-nk-cocoa/assets/cocoa_map_threshold_065")
207
+ .rename("Cocoa_ETH")
208
+ .selfMask()
198
209
  )
199
210
 
200
211
 
@@ -212,7 +223,7 @@ def g_fdap_palm_prep():
212
223
  .mosaic()
213
224
  .gt(0.88) # Precision and recall ~78% at 0.88 threshold.
214
225
  )
215
- return fdap_palm.rename("Oil_palm_FDaP")
226
+ return fdap_palm.rename("Oil_palm_FDaP").selfMask()
216
227
 
217
228
 
218
229
  def g_fdap_palm_2023_prep():
@@ -224,7 +235,7 @@ def g_fdap_palm_2023_prep():
224
235
  .mosaic()
225
236
  .gt(0.88) # Precision and recall ~78% at 0.88 threshold.
226
237
  )
227
- return fdap_palm.rename("Oil_palm_2023_FDaP")
238
+ return fdap_palm.rename("Oil_palm_2023_FDaP").selfMask()
228
239
 
229
240
 
230
241
  # Cocoa FDaP
@@ -237,7 +248,7 @@ def g_fdap_cocoa_prep():
237
248
  .mosaic()
238
249
  .gt(0.96) # Precision and recall ~87% 0.96 threshold.
239
250
  )
240
- return fdap_cocoa.rename("Cocoa_FDaP")
251
+ return fdap_cocoa.rename("Cocoa_FDaP").selfMask()
241
252
 
242
253
 
243
254
  def g_fdap_cocoa_2023_prep():
@@ -249,7 +260,7 @@ def g_fdap_cocoa_2023_prep():
249
260
  .mosaic()
250
261
  .gt(0.96) # Precision and recall ~87% 0.96 threshold.
251
262
  )
252
- return fdap_cocoa.rename("Cocoa_2023_FDaP")
263
+ return fdap_cocoa.rename("Cocoa_2023_FDaP").selfMask()
253
264
 
254
265
 
255
266
  # Rubber FDaP
@@ -262,7 +273,7 @@ def g_fdap_rubber_prep():
262
273
  .mosaic()
263
274
  .gt(0.59) # Precision and recall ~80% 0.59 threshold.
264
275
  )
265
- return fdap_rubber.rename("Rubber_FDaP")
276
+ return fdap_rubber.rename("Rubber_FDaP").selfMask()
266
277
 
267
278
 
268
279
  def g_fdap_rubber_2023_prep():
@@ -274,7 +285,7 @@ def g_fdap_rubber_2023_prep():
274
285
  .mosaic()
275
286
  .gt(0.59) # Threshold for Rubber
276
287
  )
277
- return fdap_rubber.rename("Rubber_2023_FDaP")
288
+ return fdap_rubber.rename("Rubber_2023_FDaP").selfMask()
278
289
 
279
290
 
280
291
  # # Coffee FDaP
@@ -291,7 +302,7 @@ def g_fdap_coffee_2020_prep():
291
302
  .gt(0.99) # Precision and recall ~54% 0.99 threshold.
292
303
  )
293
304
 
294
- return coffee_2020.rename("Coffee_FDaP")
305
+ return coffee_2020.rename("Coffee_FDaP").selfMask()
295
306
 
296
307
 
297
308
  def g_fdap_coffee_2023_prep():
@@ -306,7 +317,7 @@ def g_fdap_coffee_2023_prep():
306
317
  .mosaic()
307
318
  .gt(0.99) # Precision and recall ~54% 0.99 threshold.
308
319
  )
309
- return coffee_2023.rename("Coffee_FDaP_2023")
320
+ return coffee_2023.rename("Coffee_FDaP_2023").selfMask()
310
321
 
311
322
 
312
323
  # Rubber_RBGE - from Royal Botanical Gardens of Edinburgh (RBGE) NB for 2021
@@ -315,14 +326,16 @@ def g_rbge_rubber_prep():
315
326
  ee.Image(
316
327
  "users/wangyxtina/MapRubberPaper/rRubber10m202122_perc1585DifESAdist5pxPF"
317
328
  )
318
- .unmask()
319
329
  .rename("Rubber_RBGE")
330
+ .selfMask()
320
331
  )
321
332
 
322
333
 
323
334
  # soy 2020 South America
324
335
  def g_soy_song_2020_prep():
325
- return ee.Image("projects/glad/soy_annual_SA/2020").unmask().rename("Soy_Song_2020")
336
+ return (
337
+ ee.Image("projects/glad/soy_annual_SA/2020").rename("Soy_Song_2020").selfMask()
338
+ )
326
339
 
327
340
 
328
341
  ##############
@@ -336,49 +349,24 @@ def g_esri_2023_tc_prep():
336
349
  esri_lulc10_TC = (
337
350
  esri_lulc10_raw.filterDate("2023-01-01", "2023-12-31").mosaic().eq(2)
338
351
  )
339
- return esri_lulc10_TC.rename("ESRI_2023_TC")
352
+ return esri_lulc10_TC.rename("ESRI_2023_TC").selfMask()
340
353
 
341
354
 
342
355
  # ESRI 2023 - Crop
343
- def g_esri_2023_crop_prep():
356
+ def g_esri_2020_2023_crop_prep():
344
357
  esri_lulc10_raw = ee.ImageCollection(
345
358
  "projects/sat-io/open-datasets/landcover/ESRI_Global-LULC_10m_TS"
346
359
  )
347
- esri_lulc10_crop = (
348
- esri_lulc10_raw.filterDate("2023-01-01", "2023-12-31").mosaic().eq(5)
360
+ esri_lulc10_crop_2020 = (
361
+ esri_lulc10_raw.filterDate("2020-01-01", "2020-12-31").mosaic().eq(5)
349
362
  )
350
- return esri_lulc10_crop.rename("ESRI_2023_crop")
351
-
352
-
353
- # GLC_FCS30D 2022
354
-
355
- # GLC_FCS30D Tree Cover
356
- # forest classes + swamp + mangrove / what to do with shrubland?
357
- def g_glc_fcs30d_tc_2022_prep():
358
- GLC_FCS30D = (
359
- ee.ImageCollection("projects/sat-io/open-datasets/GLC-FCS30D/annual")
360
- .mosaic()
361
- .select(22)
362
- )
363
- GLC_FCS30D_TC = (
364
- (GLC_FCS30D.gte(51))
365
- .And(GLC_FCS30D.lte(92))
366
- .Or(GLC_FCS30D.eq(181))
367
- .Or(GLC_FCS30D.eq(185))
363
+ esri_lulc10_crop_2023 = (
364
+ esri_lulc10_raw.filterDate("2023-01-01", "2023-12-31").mosaic().eq(5)
368
365
  )
369
- return GLC_FCS30D_TC.rename("GLC_FCS30D_TC_2022")
370
366
 
367
+ newCrop = esri_lulc10_crop_2023.And(esri_lulc10_crop_2020.Not())
371
368
 
372
- # GLC_FCS30D crop
373
- # 10 Rainfed cropland; 11 Herbaceous cover; 12 Tree or shrub cover (Orchard); 20 Irrigated cropland
374
- def g_glc_fcs30d_crop_2022_prep():
375
- GLC_FCS30D = (
376
- ee.ImageCollection("projects/sat-io/open-datasets/GLC-FCS30D/annual")
377
- .mosaic()
378
- .select(22)
379
- )
380
- GLC_FCS30D_crop = GLC_FCS30D.gte(10).And(GLC_FCS30D.lte(20))
381
- return GLC_FCS30D_crop.rename("GLC_FCS30D_crop_2022")
369
+ return newCrop.rename("ESRI_crop_gain_2020_2023").selfMask()
382
370
 
383
371
 
384
372
  #### disturbances by year
@@ -388,23 +376,13 @@ def g_radd_year_prep():
388
376
  from datetime import datetime
389
377
 
390
378
  radd = ee.ImageCollection("projects/radar-wur/raddalert/v1")
391
-
392
379
  radd_date = (
393
380
  radd.filterMetadata("layer", "contains", "alert").select("Date").mosaic()
394
381
  )
395
- # date of avaialbility
396
- start_year = 19 ## (starts 2019 in Africa, then 2020 for S America and Asia: https://data.globalforestwatch.org/datasets/gfw::deforestation-alerts-radd/about
397
-
398
- current_year = (
399
- datetime.now().year
400
- % 100
401
- # NB the % 100 part gets last two digits needed
402
- )
382
+ start_year = 19
383
+ current_year = datetime.now().year % 100
403
384
 
404
- img_stack = None
405
- # Generate an image based on GFC with one band of forest tree loss per year from 2001 to <current year>
406
- for year in range(start_year, current_year + 1):
407
- # gfc_loss_year = gfc.select(['lossyear']).eq(i).And(gfc.select(['treecover2000']).gt(10)) # use any definition of loss
385
+ def make_band(year, img_stack):
408
386
  start = year * 1000
409
387
  end = year * 1000 + 365
410
388
  radd_year = (
@@ -413,12 +391,35 @@ def g_radd_year_prep():
413
391
  .gt(0)
414
392
  .rename("RADD_year_" + "20" + str(year))
415
393
  )
394
+ return ee.Image(img_stack).addBands(radd_year)
395
+
396
+ years = ee.List.sequence(start_year, current_year)
397
+ first_year = ee.Number(years.get(0))
398
+ start = first_year.multiply(1000)
399
+ end = first_year.multiply(1000).add(365)
400
+ band_name = ee.String("RADD_year_").cat("20").cat(first_year.format("%02d"))
401
+ first_band = (
402
+ radd_date.updateMask(radd_date.gte(start))
403
+ .updateMask(radd_date.lte(end))
404
+ .gt(0)
405
+ .rename(band_name)
406
+ )
407
+
408
+ def make_band(year, img_stack):
409
+ year_num = ee.Number(year)
410
+ start = year_num.multiply(1000)
411
+ end = year_num.multiply(1000).add(365)
412
+ band_name = ee.String("RADD_year_").cat("20").cat(year_num.format("%02d"))
413
+ radd_year = (
414
+ radd_date.updateMask(radd_date.gte(start))
415
+ .updateMask(radd_date.lte(end))
416
+ .gt(0)
417
+ .rename(band_name)
418
+ )
419
+ return ee.Image(img_stack).addBands(radd_year)
416
420
 
417
- if img_stack is None:
418
- img_stack = radd_year
419
- else:
420
- img_stack = img_stack.addBands(radd_year)
421
- return img_stack
421
+ img_stack = years.slice(1).iterate(make_band, first_band)
422
+ return ee.Image(img_stack)
422
423
 
423
424
 
424
425
  # TMF_def_2000 to TMF_def_2023
@@ -428,7 +429,9 @@ def g_tmf_def_per_year_prep():
428
429
  img_stack = None
429
430
  # Generate an image based on GFC with one band of forest tree loss per year from 2001 to 2022
430
431
  for i in range(0, 24 + 1):
431
- tmf_def_year = tmf_def.eq(2000 + i).rename("TMF_def_" + str(2000 + i))
432
+ year_num = ee.Number(2000 + i)
433
+ band_name = ee.String("TMF_def_").cat(year_num.format("%d"))
434
+ tmf_def_year = tmf_def.eq(year_num).rename(band_name)
432
435
  if img_stack is None:
433
436
  img_stack = tmf_def_year
434
437
  else:
@@ -443,7 +446,9 @@ def g_tmf_deg_per_year_prep():
443
446
  img_stack = None
444
447
  # Generate an image based on GFC with one band of forest tree loss per year from 2001 to 2022
445
448
  for i in range(0, 24 + 1):
446
- tmf_def_year = tmf_def.eq(2000 + i).rename("TMF_deg_" + str(2000 + i))
449
+ year_num = ee.Number(2000 + i)
450
+ band_name = ee.String("TMF_deg_").cat(year_num.format("%d"))
451
+ tmf_def_year = tmf_def.eq(year_num).rename(band_name)
447
452
  if img_stack is None:
448
453
  img_stack = tmf_def_year
449
454
  else:
@@ -458,10 +463,12 @@ def g_glad_gfc_loss_per_year_prep():
458
463
  img_stack = None
459
464
  # Generate an image based on GFC with one band of forest tree loss per year from 2001 to 2022
460
465
  for i in range(1, 24 + 1):
466
+ year_num = ee.Number(2000 + i)
467
+ band_name = ee.String("GFC_loss_year_").cat(year_num.format("%d"))
461
468
  gfc_loss_year = (
462
469
  gfc.select(["lossyear"]).eq(i).And(gfc.select(["treecover2000"]).gt(10))
463
470
  )
464
- gfc_loss_year = gfc_loss_year.rename("GFC_loss_year_" + str(2000 + i))
471
+ gfc_loss_year = gfc_loss_year.rename(band_name)
465
472
  if img_stack is None:
466
473
  img_stack = gfc_loss_year
467
474
  else:
@@ -482,6 +489,8 @@ def g_modis_fire_prep():
482
489
  img_stack = None
483
490
 
484
491
  for year in range(start_year, end_year + 1):
492
+ year_num = ee.Number(year)
493
+ band_name = ee.String("MODIS_fire_").cat(year_num.format("%d"))
485
494
  date_st = f"{year}-01-01"
486
495
  date_ed = f"{year}-12-31"
487
496
  modis_year = (
@@ -489,7 +498,7 @@ def g_modis_fire_prep():
489
498
  .mosaic()
490
499
  .select(["BurnDate"])
491
500
  .gte(0)
492
- .rename(f"MODIS_fire_{year}")
501
+ .rename(band_name)
493
502
  )
494
503
  img_stack = modis_year if img_stack is None else img_stack.addBands(modis_year)
495
504
 
@@ -509,6 +518,8 @@ def g_esa_fire_prep():
509
518
  img_stack = None
510
519
 
511
520
  for year in range(start_year, end_year + 1):
521
+ year_num = ee.Number(year)
522
+ band_name = ee.String("ESA_fire_").cat(year_num.format("%d"))
512
523
  date_st = f"{year}-01-01"
513
524
  date_ed = f"{year}-12-31"
514
525
  esa_year = (
@@ -516,107 +527,17 @@ def g_esa_fire_prep():
516
527
  .mosaic()
517
528
  .select(["BurnDate"])
518
529
  .gte(0)
519
- .rename(f"ESA_fire_{year}")
530
+ .rename(band_name)
520
531
  )
521
532
  img_stack = esa_year if img_stack is None else img_stack.addBands(esa_year)
522
533
 
523
534
  return img_stack
524
535
 
525
536
 
526
- # # DIST_alert_2024 to DIST_alert_< current year >
527
- # # Notes:
528
- # # 1) so far only available for 2024 onwards in GEE
529
- # # TO DO - see if gee asset for pre 2020-2024 is available from GLAD team, else download from nasa and put in Whisp assets
530
- # # 2) masked alerts (as dist alerts are for all vegetation) to JRC EUFO 2020 layer, as close to EUDR definition
531
- # # TO DO - ask opinions on if others (such as treecover data from GLAD team) should be used instead
532
-
533
-
534
- # def glad_dist_year_prep():
535
-
536
- # # Load the vegetation disturbance collections
537
-
538
- # # Vegetation disturbance status (0-8, class flag, 8-bit)
539
- # VEGDISTSTATUS = ee.ImageCollection(
540
- # "projects/glad/HLSDIST/current/VEG-DIST-STATUS"
541
- # ).mosaic()
542
- # # Initial vegetation disturbance date (>0: days since 2020-12-31, 16-bit)
543
- # VEGDISTDATE = ee.ImageCollection(
544
- # "projects/glad/HLSDIST/current/VEG-DIST-DATE"
545
- # ).mosaic()
546
-
547
- # # NB relies on initial date of disturbance - consider if last date needed? : VEGLASTDATE = ee.ImageCollection("projects/glad/HLSDIST/current/VEG-LAST-DATE").mosaic(); # Last assessed observation date (≥1, days, 16-bit)
548
-
549
- # # Key for high-confidence alerts (values 3, 6, 7, 8)
550
- # high_conf_values = [3, 6, 7, 8]
551
- # # where:
552
- # # 3 = <50% loss, high confidence, ongoing
553
- # # 6 = ≥50% loss, high confidence, ongoing
554
- # # 7 = <50% loss, high confidence, finished
555
- # # 8 = ≥50% loss, high confidence, finished
556
- # # Note could use <50% loss (i.e. only 6 and 7) for if want to be more strict
557
-
558
- # # Create high-confidence mask
559
- # dist_high_conf = VEGDISTSTATUS.remap(
560
- # high_conf_values, [1] * len(high_conf_values), 0
561
- # )
562
-
563
- # # Determine start year and current year dynamically
564
- # start_year = 2024 # Set the first year of interest
565
- # current_year = datetime.now().year
566
-
567
- # # Calculate days since December 31, 2020 for start and end dates (server-side)
568
- # start_of_2020 = ee.Date("2020-12-31").millis().divide(86400000).int()
569
-
570
- # # Create a list to hold the yearly images
571
- # yearly_images = []
572
-
573
- # for year in range(start_year, current_year + 1):
574
- # start_of_year = (
575
- # ee.Date(f"{year}-01-01")
576
- # .millis()
577
- # .divide(86400000)
578
- # .int()
579
- # .subtract(start_of_2020)
580
- # )
581
- # start_of_next_year = (
582
- # ee.Date(f"{year + 1}-01-01")
583
- # .millis()
584
- # .divide(86400000)
585
- # .int()
586
- # .subtract(start_of_2020)
587
- # )
588
-
589
- # # Filter VEG-DIST-DATE for the selected year
590
- # dist_year = VEGDISTDATE.gte(start_of_year).And(
591
- # VEGDISTDATE.lt(start_of_next_year)
592
- # )
593
-
594
- # # Apply high-confidence mask and rename the band
595
- # high_conf_year = dist_year.updateMask(dist_high_conf).rename(
596
- # f"DIST_year_{year}"
597
- # )
598
-
599
- # # Append the year's data to the list
600
- # yearly_images.append(high_conf_year)
601
-
602
- # # Combine all yearly images into a single image
603
- # img_stack = ee.Image.cat(yearly_images)
604
-
605
- # # Rename the bands correctly
606
- # band_names = [f"DIST_year_{year}" for year in range(start_year, current_year + 1)]
607
- # img_stack = img_stack.select(img_stack.bandNames(), band_names)
608
-
609
- # return img_stack.updateMask(
610
- # jrc_gfc_2020_prep()
611
- # ) # mask yearly dist alerts to forest cover in 2020
612
-
613
-
614
537
  #### disturbances combined (split into before and after 2020)
615
538
 
616
539
  # RADD_after_2020
617
540
  def g_radd_after_2020_prep():
618
- from datetime import datetime
619
-
620
541
  radd = ee.ImageCollection("projects/radar-wur/raddalert/v1")
621
542
 
622
543
  radd_date = (
@@ -625,9 +546,8 @@ def g_radd_after_2020_prep():
625
546
  # date of avaialbility
626
547
  start_year = 21 ## (starts 2019 in Africa, then 2020 for S America and Asia: https://data.globalforestwatch.org/datasets/gfw::deforestation-alerts-radd/about)
627
548
 
628
- current_year = (
629
- datetime.now().year % 100
630
- ) # NB the % 100 part gets last two digits needed
549
+ # Use pre-calculated current year (avoids repeated datetime calls)
550
+ current_year = CURRENT_YEAR_2DIGIT
631
551
  start = start_year * 1000
632
552
  end = current_year * 1000 + 365
633
553
  return (
@@ -635,13 +555,11 @@ def g_radd_after_2020_prep():
635
555
  .updateMask(radd_date.lte(end))
636
556
  .gt(0)
637
557
  .rename("RADD_after_2020")
638
- )
558
+ ).selfMask()
639
559
 
640
560
 
641
561
  # RADD_before_2020
642
562
  def g_radd_before_2020_prep():
643
- from datetime import datetime
644
-
645
563
  radd = ee.ImageCollection("projects/radar-wur/raddalert/v1")
646
564
 
647
565
  radd_date = (
@@ -650,8 +568,6 @@ def g_radd_before_2020_prep():
650
568
  # date of avaialbility
651
569
  start_year = 19 ## (starts 2019 in Africa, then 2020 for S America and Asia: https://data.globalforestwatch.org/datasets/gfw::deforestation-alerts-radd/about)
652
570
 
653
- # current_year = datetime.now().year % 100 # NB the % 100 part gets last two digits needed
654
-
655
571
  start = start_year * 1000
656
572
  end = 20 * 1000 + 365
657
573
  return (
@@ -659,7 +575,7 @@ def g_radd_before_2020_prep():
659
575
  .updateMask(radd_date.lte(end))
660
576
  .gt(0)
661
577
  .rename("RADD_before_2020")
662
- )
578
+ ).selfMask()
663
579
 
664
580
 
665
581
  # # DIST_after_2020
@@ -687,25 +603,35 @@ def g_radd_before_2020_prep():
687
603
  # TMF_deg_before_2020
688
604
  def g_tmf_deg_before_2020_prep():
689
605
  tmf_deg = ee.ImageCollection("projects/JRC/TMF/v1_2024/DegradationYear").mosaic()
690
- return (tmf_deg.lte(2020)).And(tmf_deg.gte(2000)).rename("TMF_deg_before_2020")
606
+ return (
607
+ (tmf_deg.lte(2020))
608
+ .And(tmf_deg.gte(2000))
609
+ .rename("TMF_deg_before_2020")
610
+ .selfMask()
611
+ )
691
612
 
692
613
 
693
614
  # TMF_deg_after_2020
694
615
  def g_tmf_deg_after_2020_prep():
695
616
  tmf_deg = ee.ImageCollection("projects/JRC/TMF/v1_2024/DegradationYear").mosaic()
696
- return tmf_deg.gt(2020).rename("TMF_deg_after_2020")
617
+ return tmf_deg.gt(2020).rename("TMF_deg_after_2020").selfMask()
697
618
 
698
619
 
699
620
  # tmf_def_before_2020
700
621
  def g_tmf_def_before_2020_prep():
701
622
  tmf_def = ee.ImageCollection("projects/JRC/TMF/v1_2024/DeforestationYear").mosaic()
702
- return (tmf_def.lte(2020)).And(tmf_def.gte(2000)).rename("TMF_def_before_2020")
623
+ return (
624
+ (tmf_def.lte(2020))
625
+ .And(tmf_def.gte(2000))
626
+ .rename("TMF_def_before_2020")
627
+ .selfMask()
628
+ )
703
629
 
704
630
 
705
631
  # tmf_def_after_2020
706
632
  def g_tmf_def_after_2020_prep():
707
633
  tmf_def = ee.ImageCollection("projects/JRC/TMF/v1_2024/DeforestationYear").mosaic()
708
- return tmf_def.gt(2020).rename("TMF_def_after_2020")
634
+ return tmf_def.gt(2020).rename("TMF_def_after_2020").selfMask()
709
635
 
710
636
 
711
637
  # GFC_loss_before_2020 (loss within 10 percent cover; includes 2020; correct for version 11)
@@ -715,7 +641,7 @@ def g_glad_gfc_loss_before_2020_prep():
715
641
  gfc_loss = (
716
642
  gfc.select(["lossyear"]).lte(20).And(gfc.select(["treecover2000"]).gt(10))
717
643
  )
718
- return gfc_loss.rename("GFC_loss_before_2020")
644
+ return gfc_loss.rename("GFC_loss_before_2020").selfMask()
719
645
 
720
646
 
721
647
  # GFC_loss_after_2020 (loss within 10 percent cover; correct for version 11)
@@ -723,7 +649,7 @@ def g_glad_gfc_loss_after_2020_prep():
723
649
  # Load the Global Forest Change dataset
724
650
  gfc = ee.Image("UMD/hansen/global_forest_change_2024_v1_12")
725
651
  gfc_loss = gfc.select(["lossyear"]).gt(20).And(gfc.select(["treecover2000"]).gt(10))
726
- return gfc_loss.rename("GFC_loss_after_2020")
652
+ return gfc_loss.rename("GFC_loss_after_2020").selfMask()
727
653
 
728
654
 
729
655
  # MODIS_fire_before_2020
@@ -739,14 +665,15 @@ def g_modis_fire_before_2020_prep():
739
665
  .select(["BurnDate"])
740
666
  .gte(0)
741
667
  .rename("MODIS_fire_before_2020")
742
- )
668
+ ).selfMask()
743
669
 
744
670
 
745
671
  # MODIS_fire_after_2020
746
672
  def g_modis_fire_after_2020_prep():
747
673
  modis_fire = ee.ImageCollection("MODIS/061/MCD64A1")
748
674
  start_year = 2021
749
- end_year = datetime.now().year
675
+ # Use pre-calculated current year (avoids repeated datetime calls)
676
+ end_year = CURRENT_YEAR - 1 # Use year - 1 to ensure data availability
750
677
  date_st = str(start_year) + "-01-01"
751
678
  date_ed = str(end_year) + "-12-31"
752
679
  return (
@@ -755,7 +682,7 @@ def g_modis_fire_after_2020_prep():
755
682
  .select(["BurnDate"])
756
683
  .gte(0)
757
684
  .rename("MODIS_fire_after_2020")
758
- )
685
+ ).selfMask()
759
686
 
760
687
 
761
688
  # ESA_fire_before_2020
@@ -771,7 +698,7 @@ def g_esa_fire_before_2020_prep():
771
698
  .select(["BurnDate"])
772
699
  .gte(0)
773
700
  .rename("ESA_fire_before_2020")
774
- )
701
+ ).selfMask()
775
702
 
776
703
 
777
704
  #########################logging concessions
@@ -817,7 +744,7 @@ def g_logging_concessions_before_2020_prep():
817
744
  ]
818
745
  ).mosaic()
819
746
 
820
- return logging_concessions_binary.rename("GFW_logging_before_2020")
747
+ return logging_concessions_binary.rename("GFW_logging_before_2020").selfMask()
821
748
 
822
749
 
823
750
  #########################national datasets
@@ -835,7 +762,7 @@ def g_logging_concessions_before_2020_prep():
835
762
  def nbr_terraclass_amz20_primary_prep():
836
763
  tcamz20 = ee.Image("projects/ee-whisp/assets/NBR/terraclass_amz_2020")
837
764
  tcamz20_f = tcamz20.eq(1)
838
- return tcamz20_f.rename("nBR_INPE_TC_primary_forest_Amazon_2020")
765
+ return tcamz20_f.rename("nBR_INPE_TC_primary_forest_Amazon_2020").selfMask()
839
766
 
840
767
 
841
768
  # [Official NFMS dataset] Brazilian Forest Service dataset on natural forest cover from PRODES and TerraClass data, base year 2022
@@ -849,7 +776,7 @@ def nbr_bfs_ptn_f20_prep():
849
776
  bfs_fptn20 = ee.FeatureCollection("projects/ee-whisp/assets/NBR/bfs_ptn_2020")
850
777
 
851
778
  bfs_fptn20_binary = ee.Image().paint(bfs_fptn20, 1)
852
- return bfs_fptn20_binary.rename("nBR_BFS_primary_forest_Pantanal_2020")
779
+ return bfs_fptn20_binary.rename("nBR_BFS_primary_forest_Pantanal_2020").selfMask()
853
780
 
854
781
 
855
782
  # Caatinga - filtered with QGIS because the original geodatabase is too large to export as a shapefile (GEE accepted format)
@@ -857,35 +784,39 @@ def nbr_bfs_ptn_f20_prep():
857
784
  def nbr_bfs_caat_f20_prep():
858
785
  bfs_fcaat20 = ee.FeatureCollection("projects/ee-whisp/assets/NBR/bfs_caat_2020")
859
786
  bfs_fcaat20_binary = ee.Image().paint(bfs_fcaat20, 1)
860
- return bfs_fcaat20_binary.rename("nBR_BFS_primary_forest_Caatinga_2020")
787
+ return bfs_fcaat20_binary.rename("nBR_BFS_primary_forest_Caatinga_2020").selfMask()
861
788
 
862
789
 
863
790
  # Atlantic Forest - filtered with QGIS because the original geodatabase is too large to export as a shapefile (GEE accepted format)
864
791
  def nbr_bfs_atlf_f20_prep():
865
792
  bfs_fatlf20 = ee.FeatureCollection("projects/ee-whisp/assets/NBR/bfs_atlf_2020")
866
793
  bfs_fatlf20_binary = ee.Image().paint(bfs_fatlf20, 1)
867
- return bfs_fatlf20_binary.rename("nBR_BFS_primary_forest_AtlanticForest_2020")
794
+ return bfs_fatlf20_binary.rename(
795
+ "nBR_BFS_primary_forest_AtlanticForest_2020"
796
+ ).selfMask()
868
797
 
869
798
 
870
799
  # Pampa - filtered in QGIS to save some storage space
871
800
  def nbr_bfs_pmp_f20_prep():
872
801
  bfs_fpmp20 = ee.FeatureCollection("projects/ee-whisp/assets/NBR/bfs_pmp_2020")
873
802
  bfs_fpmp20_binary = ee.Image().paint(bfs_fpmp20, 1)
874
- return bfs_fpmp20_binary.rename("nBR_BFS_primary_forest_Pampa_2020")
803
+ return bfs_fpmp20_binary.rename("nBR_BFS_primary_forest_Pampa_2020").selfMask()
875
804
 
876
805
 
877
806
  ##########################secondary forests###############################################
878
807
  def nbr_terraclass_amz20_secondary_prep():
879
808
  tcamz20 = ee.Image("projects/ee-whisp/assets/NBR/terraclass_amz_2020")
880
809
  tcamz20_f = tcamz20.eq(2)
881
- return tcamz20_f.rename("nBR_INPE_TC_secondary_forest_Amazon_2020")
810
+ return tcamz20_f.rename("nBR_INPE_TC_secondary_forest_Amazon_2020").selfMask()
882
811
 
883
812
 
884
813
  # Cerrado - filtered with QGIS because the original geodatabase is too large to export as a shapefile (GEE accepted format)
885
814
  def nbr_bfs_cer_f20_prep():
886
815
  bfs_fcer20 = ee.FeatureCollection("projects/ee-whisp/assets/NBR/bfs_cerr_2020")
887
816
  bfs_fcer20_binary = ee.Image().paint(bfs_fcer20, 1)
888
- return bfs_fcer20_binary.rename("nBR_BFS_primary_and_secondary_forest_Cerrado_2020")
817
+ return bfs_fcer20_binary.rename(
818
+ "nBR_BFS_primary_and_secondary_forest_Cerrado_2020"
819
+ ).selfMask()
889
820
 
890
821
 
891
822
  # %%
@@ -904,7 +835,9 @@ def nbr_mapbiomasc9_f20_prep():
904
835
  .Or(mapbiomasc9_20.eq(6))
905
836
  .Or(mapbiomasc9_20.eq(49))
906
837
  )
907
- return mapbiomasc9_20_forest.rename("nBR_MapBiomas_col9_forest_Brazil_2020")
838
+ return mapbiomasc9_20_forest.rename(
839
+ "nBR_MapBiomas_col9_forest_Brazil_2020"
840
+ ).selfMask()
908
841
 
909
842
 
910
843
  # ### ########################NBR plantation forest in 2020:#######################################
@@ -915,7 +848,7 @@ def nbr_mapbiomasc9_f20_prep():
915
848
  def nbr_terraclass_amz20_silv_prep():
916
849
  tcamz20 = ee.Image("projects/ee-whisp/assets/NBR/terraclass_amz_2020")
917
850
  tcamz20_silviculture = tcamz20.eq(9)
918
- return tcamz20_silviculture.rename("nBR_INPE_TCsilviculture_Amazon_2020")
851
+ return tcamz20_silviculture.rename("nBR_INPE_TCsilviculture_Amazon_2020").selfMask()
919
852
 
920
853
 
921
854
  # [Official NFMS dataset] INPE/EMBRAPA TerraClass land use/cover in the Cerrado biome, 2020
@@ -924,7 +857,9 @@ def nbr_terraclass_amz20_silv_prep():
924
857
  def nbr_terraclass_silv_cer20_prep():
925
858
  tccer20 = ee.Image("projects/ee-whisp/assets/NBR/terraclass_cer_2020")
926
859
  tccer20_silviculture = tccer20.eq(9)
927
- return tccer20_silviculture.rename("nBR_INPE_TCsilviculture_Cerrado_2020")
860
+ return tccer20_silviculture.rename(
861
+ "nBR_INPE_TCsilviculture_Cerrado_2020"
862
+ ).selfMask()
928
863
 
929
864
 
930
865
  # [non-official dataset by MapBiomas multisector initiative]
@@ -938,7 +873,7 @@ def nbr_mapbiomasc9_silv20_prep():
938
873
  mapbiomasc9_20_silviculture = mapbiomasc9_20.eq(9)
939
874
  return mapbiomasc9_20_silviculture.rename(
940
875
  "nBR_MapBiomas_col9_silviculture_Brazil_2020"
941
- )
876
+ ).selfMask()
942
877
 
943
878
 
944
879
  ################ ### NBR Disturbances before 2020:########################################
@@ -983,8 +918,9 @@ def nbr_prodes_before_2020_prep():
983
918
  prodes_before_20_mask = prodes.remap(
984
919
  prodes_before_20_dn, [1] * len(prodes_before_20_dn)
985
920
  ) # .eq(1)
986
- prodes_before_20 = prodes_before_20_mask.selfMask()
987
- return prodes_before_20.rename("nBR_PRODES_deforestation_Brazil_before_2020")
921
+ return prodes_before_20_mask.rename(
922
+ "nBR_PRODES_deforestation_Brazil_before_2020"
923
+ ).selfMask()
988
924
 
989
925
 
990
926
  ## Caution: 1) includes deforestation and conversion of other wooded land and grassland
@@ -1009,7 +945,9 @@ def nbr_deter_amazon_before_2020_prep():
1009
945
  ).filter(ee.Filter.lt("formatted_date", ee.Date("2020-12-31")))
1010
946
 
1011
947
  deter_deg_binary = ee.Image().paint(deter_deg, 1)
1012
- return deter_deg_binary.rename("nBR_DETER_forestdegradation_Amazon_before_2020")
948
+ return deter_deg_binary.rename(
949
+ "nBR_DETER_forestdegradation_Amazon_before_2020"
950
+ ).selfMask()
1013
951
 
1014
952
 
1015
953
  ################ ### NBR Disturbances after 2020:########################################
@@ -1026,7 +964,9 @@ def nbr_prodes_after_2020_prep():
1026
964
  prodes_after_20_dn, [1] * len(prodes_after_20_dn)
1027
965
  ) # .eq(1)
1028
966
  prodes_after_20 = prodes_after_20_mask.selfMask()
1029
- return prodes_after_20.rename("nBR_PRODES_deforestation_Brazil_after_2020")
967
+ return prodes_after_20.rename(
968
+ "nBR_PRODES_deforestation_Brazil_after_2020"
969
+ ).selfMask()
1030
970
 
1031
971
 
1032
972
  # %%
@@ -1048,7 +988,9 @@ def nbr_deter_amazon_after_2020_prep():
1048
988
  ).filter(ee.Filter.gt("formatted_date", ee.Date("2021-01-01")))
1049
989
 
1050
990
  deter_deg_binary = ee.Image().paint(deter_deg, 1)
1051
- return deter_deg_binary.rename("nBR_DETER_forestdegradation_Amazon_after_2020")
991
+ return deter_deg_binary.rename(
992
+ "nBR_DETER_forestdegradation_Amazon_after_2020"
993
+ ).selfMask()
1052
994
 
1053
995
 
1054
996
  # ########################## NBR commodities - permanent/perennial crops in 2020:###############################
@@ -1062,7 +1004,7 @@ def nbr_terraclass_amz_cer20_pc_prep():
1062
1004
  tccer20 = ee.Image("projects/ee-whisp/assets/NBR/terraclass_cer_2020")
1063
1005
  tccer20_pc = tccer20.eq(12).Or(tccer20.eq(13))
1064
1006
  tc_pc = ee.ImageCollection([tcamz20_pc, tccer20_pc]).mosaic()
1065
- return tc_pc.rename("nBR_INPE_TCamz_cer_perennial_2020")
1007
+ return tc_pc.rename("nBR_INPE_TCamz_cer_perennial_2020").selfMask()
1066
1008
 
1067
1009
 
1068
1010
  # [non-official dataset by MapBiomas multisector initiative]
@@ -1074,7 +1016,7 @@ def nbr_mapbiomasc9_cof_prep():
1074
1016
  "projects/mapbiomas-public/assets/brazil/lulc/collection9/mapbiomas_collection90_integration_v1"
1075
1017
  ).select("classification_2020")
1076
1018
  mapbiomasc9_20_coffee = mapbiomasc9_20.eq(46)
1077
- return mapbiomasc9_20_coffee.rename("nBR_MapBiomas_col9_coffee_2020")
1019
+ return mapbiomasc9_20_coffee.rename("nBR_MapBiomas_col9_coffee_2020").selfMask()
1078
1020
 
1079
1021
 
1080
1022
  # [non-official dataset by MapBiomas multisector initiative]
@@ -1086,7 +1028,7 @@ def nbr_mapbiomasc9_po_prep():
1086
1028
  "projects/mapbiomas-public/assets/brazil/lulc/collection9/mapbiomas_collection90_integration_v1"
1087
1029
  ).select("classification_2020")
1088
1030
  mapbiomasc9_20_palm = mapbiomasc9_20.eq(35)
1089
- return mapbiomasc9_20_palm.rename("nBR_MapBiomas_col9_palmoil_2020")
1031
+ return mapbiomasc9_20_palm.rename("nBR_MapBiomas_col9_palmoil_2020").selfMask()
1090
1032
 
1091
1033
 
1092
1034
  # [non-official dataset by MapBiomas multisector initiative]
@@ -1098,7 +1040,7 @@ def nbr_mapbiomasc9_pc_prep():
1098
1040
  "projects/mapbiomas-public/assets/brazil/lulc/collection9/mapbiomas_collection90_integration_v1"
1099
1041
  ).select("classification_2020")
1100
1042
  mapbiomasc9_20_pc = mapbiomasc9_20.eq(35).Or(mapbiomasc9_20.eq(46))
1101
- return mapbiomasc9_20_pc.rename("nBR_MapBiomas_col9_pc_2020")
1043
+ return mapbiomasc9_20_pc.rename("nBR_MapBiomas_col9_pc_2020").selfMask()
1102
1044
 
1103
1045
 
1104
1046
  # ######################## NBR commodities - annual crops in 2020:##############################
@@ -1114,7 +1056,7 @@ def nbr_terraclass_amz_cer20_ac_prep():
1114
1056
  tccer20 = ee.Image("projects/ee-whisp/assets/NBR/terraclass_cer_2020")
1115
1057
  tccer20_ac = tccer20.eq(14).Or(tccer20.eq(15))
1116
1058
  tc_ac = ee.ImageCollection([tcamz20_ac, tccer20_ac]).mosaic()
1117
- return tc_ac.rename("nBR_INPE_TCamz_cer_annual_2020")
1059
+ return tc_ac.rename("nBR_INPE_TCamz_cer_annual_2020").selfMask()
1118
1060
 
1119
1061
 
1120
1062
  # [non-official dataset by MapBiomas multisector initiative]
@@ -1126,7 +1068,7 @@ def nbr_mapbiomasc9_soy_prep():
1126
1068
  "projects/mapbiomas-public/assets/brazil/lulc/collection9/mapbiomas_collection90_integration_v1"
1127
1069
  ).select("classification_2020")
1128
1070
  mapbiomasc9_20_soy = mapbiomasc9_20.eq(39)
1129
- return mapbiomasc9_20_soy.rename("nBR_MapBiomas_col9_soy_2020")
1071
+ return mapbiomasc9_20_soy.rename("nBR_MapBiomas_col9_soy_2020").selfMask()
1130
1072
 
1131
1073
 
1132
1074
  # [non-official dataset by MapBiomas multisector initiative]
@@ -1146,7 +1088,7 @@ def nbr_mapbiomasc9_ac_prep():
1146
1088
  .Or(mapbiomasc9_20.eq(40))
1147
1089
  .Or(mapbiomasc9_20.eq(62))
1148
1090
  )
1149
- return mapbiomasc9_20_ac.rename("nBR_MapBiomas_col9_annual_crops_2020")
1091
+ return mapbiomasc9_20_ac.rename("nBR_MapBiomas_col9_annual_crops_2020").selfMask()
1150
1092
 
1151
1093
 
1152
1094
  # ################################### NBR commodities - pasture/livestock in 2020:##############################
@@ -1159,7 +1101,7 @@ def nbr_mapbiomasc9_ac_prep():
1159
1101
  def nbr_terraclass_amz20_pasture_prep():
1160
1102
  tcamz20 = ee.Image("projects/ee-whisp/assets/NBR/terraclass_amz_2020")
1161
1103
  tcamz20_pasture = tcamz20.eq(10).Or(tcamz20.eq(11))
1162
- return tcamz20_pasture.rename("nBR_INPE_TCamz_pasture_2020")
1104
+ return tcamz20_pasture.rename("nBR_INPE_TCamz_pasture_2020").selfMask()
1163
1105
 
1164
1106
 
1165
1107
  # %%
@@ -1171,7 +1113,7 @@ def nbr_terraclass_amz20_pasture_prep():
1171
1113
  def nbr_terraclass_cer20_ac_prep():
1172
1114
  tccer20 = ee.Image("projects/ee-whisp/assets/NBR/terraclass_cer_2020")
1173
1115
  tccer20_pasture = tccer20.eq(11)
1174
- return tccer20_pasture.rename("nBR_INPE_TCcer_pasture_2020")
1116
+ return tccer20_pasture.rename("nBR_INPE_TCcer_pasture_2020").selfMask()
1175
1117
 
1176
1118
 
1177
1119
  # %%
@@ -1184,7 +1126,7 @@ def nbr_mapbiomasc9_pasture_prep():
1184
1126
  "projects/mapbiomas-public/assets/brazil/lulc/collection9/mapbiomas_collection90_integration_v1"
1185
1127
  ).select("classification_2020")
1186
1128
  mapbiomasc9_20_pasture = mapbiomasc9_20.eq(15)
1187
- return mapbiomasc9_20_pasture.rename("nBR_MapBiomas_col9_pasture_2020")
1129
+ return mapbiomasc9_20_pasture.rename("nBR_MapBiomas_col9_pasture_2020").selfMask()
1188
1130
 
1189
1131
 
1190
1132
  ###################################################################
@@ -1194,13 +1136,13 @@ def nbr_mapbiomasc9_pasture_prep():
1194
1136
  def nco_ideam_forest_2020_prep():
1195
1137
  ideam_forest_raw = ee.Image("projects/ee-whisp/assets/nCO/ideam_2020_geo")
1196
1138
  ideam_forest = ideam_forest_raw.eq(1) # get forest class
1197
- return ideam_forest.rename("nCO_ideam_forest_2020")
1139
+ return ideam_forest.rename("nCO_ideam_forest_2020").selfMask()
1198
1140
 
1199
1141
 
1200
1142
  def nco_ideam_eufo_commission_2020_prep():
1201
1143
  ideam_agroforest_raw = ee.Image("projects/ee-whisp/assets/nCO/ideam_2020_geo_EUFO")
1202
1144
  ideam_agroforest = ideam_agroforest_raw.eq(4) # get forest class
1203
- return ideam_agroforest.rename("nCO_ideam_eufo_commission_2020")
1145
+ return ideam_agroforest.rename("nCO_ideam_eufo_commission_2020").selfMask()
1204
1146
 
1205
1147
 
1206
1148
  # Cocoa_bnetd
@@ -1210,43 +1152,61 @@ def nci_ocs2020_prep():
1210
1152
  .select("classification")
1211
1153
  .eq(9)
1212
1154
  .rename("nCI_Cocoa_bnetd")
1213
- ) # cocoa from national land cover map for Côte d'Ivoire
1155
+ ).selfMask() # cocoa from national land cover map for Côte d'Ivoire
1214
1156
 
1215
1157
 
1216
1158
  ###Combining datasets
1217
1159
 
1218
1160
 
1219
- def combine_datasets(national_codes=None):
1220
- """Combines datasets into a single multiband image, with fallback if assets are missing."""
1161
+ def combine_datasets(national_codes=None, validate_bands=False):
1162
+ """
1163
+ Combines datasets into a single multiband image, with fallback if assets are missing.
1164
+
1165
+ Parameters
1166
+ ----------
1167
+ national_codes : list, optional
1168
+ List of ISO2 country codes to include national datasets
1169
+ validate_bands : bool, optional
1170
+ If True, validates band names with a slow .getInfo() call (default: False)
1171
+ Only enable for debugging. Normal operation relies on exception handling.
1172
+
1173
+ Returns
1174
+ -------
1175
+ ee.Image
1176
+ Combined multiband image with all datasets
1177
+ """
1221
1178
  img_combined = ee.Image(1).rename(geometry_area_column)
1222
1179
 
1223
1180
  # Combine images directly
1224
1181
  for img in [func() for func in list_functions(national_codes=national_codes)]:
1225
1182
  try:
1226
1183
  img_combined = img_combined.addBands(img)
1184
+ # img_combined = img_combined.addBands(img)
1227
1185
  except ee.EEException as e:
1228
1186
  # logger.error(f"Error adding image: {e}")
1229
1187
  print(f"Error adding image: {e}")
1230
1188
 
1231
- try:
1232
- # Attempt to print band names to check for errors
1233
- # print(img_combined.bandNames().getInfo())
1234
- img_combined.bandNames().getInfo()
1235
-
1236
- except ee.EEException as e:
1237
- # logger.error(f"Error printing band names: {e}")
1238
- # logger.info("Running code for filtering to only valid datasets due to error in input")
1239
- print("using valid datasets filter due to error in input")
1240
- # Validate images
1241
- images_to_test = [
1242
- func() for func in list_functions(national_codes=national_codes)
1243
- ]
1244
- valid_imgs = keep_valid_images(images_to_test) # Validate images
1245
-
1246
- # Retry combining images after validation
1247
- img_combined = ee.Image(1).rename(geometry_area_column)
1248
- for img in valid_imgs:
1249
- img_combined = img_combined.addBands(img)
1189
+ # OPTIMIZATION: Removed slow .getInfo() call for band validation
1190
+ # The validation is now optional and disabled by default
1191
+ # Image processing will fail downstream if there's an issue, which is handled by exception blocks
1192
+ if validate_bands:
1193
+ try:
1194
+ # This is SLOW - only use for debugging
1195
+ img_combined.bandNames().getInfo()
1196
+ except ee.EEException as e:
1197
+ # logger.error(f"Error validating band names: {e}")
1198
+ # logger.info("Running code for filtering to only valid datasets due to error in input")
1199
+ print("using valid datasets filter due to error in validation")
1200
+ # Validate images
1201
+ images_to_test = [
1202
+ func() for func in list_functions(national_codes=national_codes)
1203
+ ]
1204
+ valid_imgs = keep_valid_images(images_to_test) # Validate images
1205
+
1206
+ # Retry combining images after validation
1207
+ img_combined = ee.Image(1).rename(geometry_area_column)
1208
+ for img in valid_imgs:
1209
+ img_combined = img_combined.addBands(img)
1250
1210
 
1251
1211
  img_combined = img_combined.multiply(ee.Image.pixelArea())
1252
1212
  print("Whisp multiband image compiled")