openforis-whisp 0.0.1__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.
- openforis_whisp/__init__.py +68 -0
- openforis_whisp/data_conversion.py +348 -0
- openforis_whisp/datasets.py +695 -0
- openforis_whisp/logger.py +39 -0
- openforis_whisp/parameters/__init__.py +15 -0
- openforis_whisp/parameters/config_runtime.py +47 -0
- openforis_whisp/parameters/lookup_context_and_metadata.csv +13 -0
- openforis_whisp/parameters/lookup_gee_datasets.csv +155 -0
- openforis_whisp/pd_schemas.py +77 -0
- openforis_whisp/reformat.py +346 -0
- openforis_whisp/risk.py +329 -0
- openforis_whisp/stats.py +752 -0
- openforis_whisp/utils.py +154 -0
- openforis_whisp-0.0.1.dist-info/LICENSE +21 -0
- openforis_whisp-0.0.1.dist-info/METADATA +296 -0
- openforis_whisp-0.0.1.dist-info/RECORD +17 -0
- openforis_whisp-0.0.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
import ee
|
|
2
|
+
|
|
3
|
+
# ee.Authenticate()
|
|
4
|
+
# ee.Initialize()
|
|
5
|
+
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
from openforis_whisp.parameters.config_runtime import (
|
|
9
|
+
geometry_area_column,
|
|
10
|
+
) # ideally make relative import statement
|
|
11
|
+
|
|
12
|
+
import inspect
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
# Configure logging
|
|
18
|
+
logging.basicConfig(
|
|
19
|
+
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_logger(name):
|
|
24
|
+
return logging.getLogger(name)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Add datasets below
|
|
28
|
+
|
|
29
|
+
# tree cover datasets
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# ESA_TC_2020
|
|
33
|
+
def esa_worldcover_trees_prep():
|
|
34
|
+
esa_worldcover_2020_raw = ee.Image("ESA/WorldCover/v100/2020")
|
|
35
|
+
esa_worldcover_trees_2020 = esa_worldcover_2020_raw.eq(95).Or(
|
|
36
|
+
esa_worldcover_2020_raw.eq(10)
|
|
37
|
+
) # get trees and mnangroves
|
|
38
|
+
return esa_worldcover_trees_2020.rename("ESA_TC_2020")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# EUFO_2020
|
|
42
|
+
def jrc_gfc_2020_prep():
|
|
43
|
+
jrc_gfc2020_raw = ee.ImageCollection("JRC/GFC2020/V2")
|
|
44
|
+
return jrc_gfc2020_raw.mosaic().rename("EUFO_2020")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# JAXA_FNF_2020
|
|
48
|
+
def jaxa_forest_prep():
|
|
49
|
+
jaxa_forest_non_forest_raw = ee.ImageCollection("JAXA/ALOS/PALSAR/YEARLY/FNF4")
|
|
50
|
+
jaxa_forest_non_forest_2020 = (
|
|
51
|
+
jaxa_forest_non_forest_raw.filterDate("2020-01-01", "2020-12-31")
|
|
52
|
+
.select("fnf")
|
|
53
|
+
.mosaic()
|
|
54
|
+
)
|
|
55
|
+
return jaxa_forest_non_forest_2020.lte(2).rename("JAXA_FNF_2020")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# GFC_TC_2020
|
|
59
|
+
def glad_gfc_10pc_prep():
|
|
60
|
+
gfc = ee.Image("UMD/hansen/global_forest_change_2023_v1_11")
|
|
61
|
+
gfc_treecover2000 = gfc.select(["treecover2000"])
|
|
62
|
+
gfc_loss2001_2020 = gfc.select(["lossyear"]).lte(20)
|
|
63
|
+
gfc_treecover2020 = gfc_treecover2000.where(gfc_loss2001_2020.eq(1), 0)
|
|
64
|
+
return gfc_treecover2020.gt(10).rename("GFC_TC_2020")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# GLAD_Primary
|
|
68
|
+
def glad_pht_prep():
|
|
69
|
+
primary_ht_forests2001_raw = ee.ImageCollection(
|
|
70
|
+
"UMD/GLAD/PRIMARY_HUMID_TROPICAL_FORESTS/v1"
|
|
71
|
+
)
|
|
72
|
+
primary_ht_forests2001 = (
|
|
73
|
+
primary_ht_forests2001_raw.select("Primary_HT_forests").mosaic().selfMask()
|
|
74
|
+
)
|
|
75
|
+
gfc = ee.Image("UMD/hansen/global_forest_change_2023_v1_11")
|
|
76
|
+
gfc_loss2001_2020 = gfc.select(["lossyear"]).lte(20)
|
|
77
|
+
return primary_ht_forests2001.where(gfc_loss2001_2020.eq(1), 0).rename(
|
|
78
|
+
"GLAD_Primary"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# TMF_undist (undistrubed forest in 2020)
|
|
83
|
+
def jrc_tmf_undisturbed_prep():
|
|
84
|
+
TMF_undist_2020 = (
|
|
85
|
+
ee.ImageCollection("projects/JRC/TMF/v1_2023/AnnualChanges")
|
|
86
|
+
.select("Dec2020")
|
|
87
|
+
.mosaic()
|
|
88
|
+
.eq(1)
|
|
89
|
+
) # update from https://github.com/forestdatapartnership/whisp/issues/42
|
|
90
|
+
return TMF_undist_2020.rename("TMF_undist")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Forest Persistence FDaP
|
|
94
|
+
def fdap_forest_prep():
|
|
95
|
+
fdap_forest_raw = ee.Image(
|
|
96
|
+
"projects/forestdatapartnership/assets/community_forests/ForestPersistence_2020"
|
|
97
|
+
)
|
|
98
|
+
fdap_forest = fdap_forest_raw.gt(0.75)
|
|
99
|
+
return fdap_forest.rename("Forest_FDaP")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
############plantation data
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# TMF_plant (plantations in 2020)
|
|
106
|
+
def jrc_tmf_plantation_prep():
|
|
107
|
+
transition = ee.ImageCollection(
|
|
108
|
+
"projects/JRC/TMF/v1_2023/TransitionMap_Subtypes"
|
|
109
|
+
).mosaic()
|
|
110
|
+
deforestation_year = ee.ImageCollection(
|
|
111
|
+
"projects/JRC/TMF/v1_2023/DeforestationYear"
|
|
112
|
+
).mosaic()
|
|
113
|
+
plantation = (transition.gte(81)).And(transition.lte(86))
|
|
114
|
+
plantation_2020 = plantation.where(
|
|
115
|
+
deforestation_year.gte(2021), 0
|
|
116
|
+
) # update from https://github.com/forestdatapartnership/whisp/issues/42
|
|
117
|
+
return plantation_2020.rename("TMF_plant")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# # Oil_palm_Descals
|
|
121
|
+
# NB updated to Descals et al 2024 paper (as opposed to Descals et al 2021 paper)
|
|
122
|
+
def creaf_descals_palm_prep():
|
|
123
|
+
# Load the Global Oil Palm Year of Plantation image and mosaic it
|
|
124
|
+
img = (
|
|
125
|
+
ee.ImageCollection(
|
|
126
|
+
"projects/ee-globaloilpalm/assets/shared/GlobalOilPalm_YoP_2021"
|
|
127
|
+
)
|
|
128
|
+
.mosaic()
|
|
129
|
+
.select("minNBR_date")
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Calculate the year of plantation and select all below and including 2020
|
|
133
|
+
oil_palm_plantation_year = img.divide(365).add(1970).floor().lte(2020)
|
|
134
|
+
|
|
135
|
+
# Create a mask for plantations in the year 2020 or earlier
|
|
136
|
+
plantation_2020 = oil_palm_plantation_year.lte(2020).selfMask()
|
|
137
|
+
return plantation_2020.rename("Oil_palm_Descals")
|
|
138
|
+
|
|
139
|
+
# Calculate the year of plantation
|
|
140
|
+
oil_palm_plantation_year = img.divide(365).add(1970).floor().lte(2020)
|
|
141
|
+
|
|
142
|
+
# Create a mask for plantations in the year 2020 or earlier
|
|
143
|
+
plantation_2020 = oil_palm_plantation_year.lte(2020).selfMask()
|
|
144
|
+
return plantation_2020.rename("Oil_palm_Descals")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# Cocoa_ETH
|
|
148
|
+
def eth_kalischek_cocoa_prep():
|
|
149
|
+
return ee.Image("projects/ee-nk-cocoa/assets/cocoa_map_threshold_065").rename(
|
|
150
|
+
"Cocoa_ETH"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# Oil Palm FDaP
|
|
155
|
+
def fdap_palm_prep():
|
|
156
|
+
fdap_palm2020_model_raw = ee.ImageCollection(
|
|
157
|
+
"projects/forestdatapartnership/assets/palm/model_2024a"
|
|
158
|
+
)
|
|
159
|
+
fdap_palm = (
|
|
160
|
+
fdap_palm2020_model_raw.filterDate("2020-01-01", "2020-12-31")
|
|
161
|
+
.mosaic()
|
|
162
|
+
.gt(0.83) # Threshold for Oil Palm
|
|
163
|
+
)
|
|
164
|
+
return fdap_palm.rename("Oil_palm_FDaP")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Rubber FDaP
|
|
168
|
+
def fdap_rubber_prep():
|
|
169
|
+
fdap_rubber2020_model_raw = ee.ImageCollection(
|
|
170
|
+
"projects/forestdatapartnership/assets/rubber/model_2024a"
|
|
171
|
+
)
|
|
172
|
+
fdap_rubber = (
|
|
173
|
+
fdap_rubber2020_model_raw.filterDate("2020-01-01", "2020-12-31")
|
|
174
|
+
.mosaic()
|
|
175
|
+
.gt(0.93) # Threshold for Rubber
|
|
176
|
+
)
|
|
177
|
+
return fdap_rubber.rename("Rubber_FDaP")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# Cocoa FDaP
|
|
181
|
+
def fdap_cocoa_prep():
|
|
182
|
+
fdap_cocoa2020_model_raw = ee.ImageCollection(
|
|
183
|
+
"projects/forestdatapartnership/assets/cocoa/model_2024a"
|
|
184
|
+
)
|
|
185
|
+
fdap_cocoa = (
|
|
186
|
+
fdap_cocoa2020_model_raw.filterDate("2020-01-01", "2020-12-31")
|
|
187
|
+
.mosaic()
|
|
188
|
+
.gt(0.5) # Threshold for Cocoa
|
|
189
|
+
)
|
|
190
|
+
return fdap_cocoa.rename("Cocoa_FDaP")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Cocoa_bnetd
|
|
194
|
+
def civ_ocs2020_prep():
|
|
195
|
+
return (
|
|
196
|
+
ee.Image("BNETD/land_cover/v1/2020")
|
|
197
|
+
.select("classification")
|
|
198
|
+
.eq(9)
|
|
199
|
+
.rename("Cocoa_bnetd")
|
|
200
|
+
) # cocoa from national land cover map for Côte d'Ivoire
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# Rubber_RBGE - from Royal Botanical Gardens of Edinburgh (RBGE) NB for 2021
|
|
204
|
+
def rbge_rubber_prep():
|
|
205
|
+
return (
|
|
206
|
+
ee.Image(
|
|
207
|
+
"users/wangyxtina/MapRubberPaper/rRubber10m202122_perc1585DifESAdist5pxPF"
|
|
208
|
+
)
|
|
209
|
+
.unmask()
|
|
210
|
+
.rename("Rubber_RBGE")
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
#### disturbances by year
|
|
215
|
+
|
|
216
|
+
# RADD_year_2019 to RADD_year_< current year >
|
|
217
|
+
def radd_year_prep():
|
|
218
|
+
from datetime import datetime
|
|
219
|
+
|
|
220
|
+
radd = ee.ImageCollection("projects/radar-wur/raddalert/v1")
|
|
221
|
+
|
|
222
|
+
radd_date = (
|
|
223
|
+
radd.filterMetadata("layer", "contains", "alert").select("Date").mosaic()
|
|
224
|
+
)
|
|
225
|
+
# date of avaialbility
|
|
226
|
+
start_year = 19 ## (starts 2019 in Africa, then 2020 for S America and Asia: https://data.globalforestwatch.org/datasets/gfw::deforestation-alerts-radd/about
|
|
227
|
+
|
|
228
|
+
current_year = (
|
|
229
|
+
datetime.now().year
|
|
230
|
+
% 100
|
|
231
|
+
# NB the % 100 part gets last two digits needed
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
img_stack = None
|
|
235
|
+
# Generate an image based on GFC with one band of forest tree loss per year from 2001 to <current year>
|
|
236
|
+
for year in range(start_year, current_year + 1):
|
|
237
|
+
# gfc_loss_year = gfc.select(['lossyear']).eq(i).And(gfc.select(['treecover2000']).gt(10)) # use any definition of loss
|
|
238
|
+
start = year * 1000
|
|
239
|
+
end = year * 1000 + 365
|
|
240
|
+
radd_year = (
|
|
241
|
+
radd_date.updateMask(radd_date.gte(start))
|
|
242
|
+
.updateMask(radd_date.lte(end))
|
|
243
|
+
.gt(0)
|
|
244
|
+
.rename("RADD_year_" + "20" + str(year))
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
if img_stack is None:
|
|
248
|
+
img_stack = radd_year
|
|
249
|
+
else:
|
|
250
|
+
img_stack = img_stack.addBands(radd_year)
|
|
251
|
+
return img_stack
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# TMF_def_2000 to TMF_def_2023
|
|
255
|
+
def tmf_def_per_year_prep():
|
|
256
|
+
# Load the TMF Deforestation annual product
|
|
257
|
+
tmf_def = ee.ImageCollection("projects/JRC/TMF/v1_2023/DeforestationYear").mosaic()
|
|
258
|
+
img_stack = None
|
|
259
|
+
# Generate an image based on GFC with one band of forest tree loss per year from 2001 to 2022
|
|
260
|
+
for i in range(0, 23 + 1):
|
|
261
|
+
tmf_def_year = tmf_def.eq(2000 + i).rename("TMF_def_" + str(2000 + i))
|
|
262
|
+
if img_stack is None:
|
|
263
|
+
img_stack = tmf_def_year
|
|
264
|
+
else:
|
|
265
|
+
img_stack = img_stack.addBands(tmf_def_year)
|
|
266
|
+
return img_stack
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# TMF_deg_2000 to TMF_deg_2023
|
|
270
|
+
def tmf_deg_per_year_prep():
|
|
271
|
+
# Load the TMF Degradation annual product
|
|
272
|
+
tmf_def = ee.ImageCollection("projects/JRC/TMF/v1_2023/DegradationYear").mosaic()
|
|
273
|
+
img_stack = None
|
|
274
|
+
# Generate an image based on GFC with one band of forest tree loss per year from 2001 to 2022
|
|
275
|
+
for i in range(0, 23 + 1):
|
|
276
|
+
tmf_def_year = tmf_def.eq(2000 + i).rename("TMF_deg_" + str(2000 + i))
|
|
277
|
+
if img_stack is None:
|
|
278
|
+
img_stack = tmf_def_year
|
|
279
|
+
else:
|
|
280
|
+
img_stack = img_stack.addBands(tmf_def_year)
|
|
281
|
+
return img_stack
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# GFC_loss_year_2001 to GFC_loss_year_2023 (correct for version 11)
|
|
285
|
+
def glad_gfc_loss_per_year_prep():
|
|
286
|
+
# Load the Global Forest Change dataset
|
|
287
|
+
gfc = ee.Image("UMD/hansen/global_forest_change_2023_v1_11")
|
|
288
|
+
img_stack = None
|
|
289
|
+
# Generate an image based on GFC with one band of forest tree loss per year from 2001 to 2022
|
|
290
|
+
for i in range(1, 23 + 1):
|
|
291
|
+
gfc_loss_year = (
|
|
292
|
+
gfc.select(["lossyear"]).eq(i).And(gfc.select(["treecover2000"]).gt(10))
|
|
293
|
+
)
|
|
294
|
+
gfc_loss_year = gfc_loss_year.rename("GFC_loss_year_" + str(2000 + i))
|
|
295
|
+
if img_stack is None:
|
|
296
|
+
img_stack = gfc_loss_year
|
|
297
|
+
else:
|
|
298
|
+
img_stack = img_stack.addBands(gfc_loss_year)
|
|
299
|
+
return img_stack
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# MODIS_fire_2000 to MODIS_fire_< current year >
|
|
303
|
+
def modis_fire_prep():
|
|
304
|
+
modis_fire = ee.ImageCollection("MODIS/061/MCD64A1")
|
|
305
|
+
start_year = 2000
|
|
306
|
+
|
|
307
|
+
# Determine the last available year by checking the latest image in the collection
|
|
308
|
+
last_image = modis_fire.sort("system:time_start", False).first()
|
|
309
|
+
last_date = ee.Date(last_image.get("system:time_start"))
|
|
310
|
+
end_year = last_date.get("year").getInfo()
|
|
311
|
+
|
|
312
|
+
img_stack = None
|
|
313
|
+
|
|
314
|
+
for year in range(start_year, end_year + 1):
|
|
315
|
+
date_st = f"{year}-01-01"
|
|
316
|
+
date_ed = f"{year}-12-31"
|
|
317
|
+
modis_year = (
|
|
318
|
+
modis_fire.filterDate(date_st, date_ed)
|
|
319
|
+
.mosaic()
|
|
320
|
+
.select(["BurnDate"])
|
|
321
|
+
.gte(0)
|
|
322
|
+
.rename(f"MODIS_fire_{year}")
|
|
323
|
+
)
|
|
324
|
+
img_stack = modis_year if img_stack is None else img_stack.addBands(modis_year)
|
|
325
|
+
|
|
326
|
+
return img_stack
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# ESA_fire_2000 to ESA_fire_2020
|
|
330
|
+
def esa_fire_prep():
|
|
331
|
+
esa_fire = ee.ImageCollection("ESA/CCI/FireCCI/5_1")
|
|
332
|
+
start_year = 2001
|
|
333
|
+
|
|
334
|
+
# Determine the last available year by checking the latest image in the collection
|
|
335
|
+
last_image = esa_fire.sort("system:time_start", False).first()
|
|
336
|
+
last_date = ee.Date(last_image.get("system:time_start"))
|
|
337
|
+
end_year = last_date.get("year").getInfo()
|
|
338
|
+
|
|
339
|
+
img_stack = None
|
|
340
|
+
|
|
341
|
+
for year in range(start_year, end_year + 1):
|
|
342
|
+
date_st = f"{year}-01-01"
|
|
343
|
+
date_ed = f"{year}-12-31"
|
|
344
|
+
esa_year = (
|
|
345
|
+
esa_fire.filterDate(date_st, date_ed)
|
|
346
|
+
.mosaic()
|
|
347
|
+
.select(["BurnDate"])
|
|
348
|
+
.gte(0)
|
|
349
|
+
.rename(f"ESA_fire_{year}")
|
|
350
|
+
)
|
|
351
|
+
img_stack = esa_year if img_stack is None else img_stack.addBands(esa_year)
|
|
352
|
+
|
|
353
|
+
return img_stack
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
# # DIST_alert_2024 to DIST_alert_< current year >
|
|
357
|
+
# # Notes:
|
|
358
|
+
# # 1) so far only available for 2024 onwards in GEE
|
|
359
|
+
# # TO DO - see if gee asset for pre 2020-2024 is available from GLAD team, else download from nasa and put in Whisp assets
|
|
360
|
+
# # 2) masked alerts (as dist alerts are for all vegetation) to JRC EUFO 2020 layer, as close to EUDR definition
|
|
361
|
+
# # TO DO - ask opinions on if others (such as treecover data from GLAD team) should be used instead
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
# def glad_dist_year_prep():
|
|
365
|
+
|
|
366
|
+
# # Load the vegetation disturbance collections
|
|
367
|
+
|
|
368
|
+
# # Vegetation disturbance status (0-8, class flag, 8-bit)
|
|
369
|
+
# VEGDISTSTATUS = ee.ImageCollection(
|
|
370
|
+
# "projects/glad/HLSDIST/current/VEG-DIST-STATUS"
|
|
371
|
+
# ).mosaic()
|
|
372
|
+
# # Initial vegetation disturbance date (>0: days since 2020-12-31, 16-bit)
|
|
373
|
+
# VEGDISTDATE = ee.ImageCollection(
|
|
374
|
+
# "projects/glad/HLSDIST/current/VEG-DIST-DATE"
|
|
375
|
+
# ).mosaic()
|
|
376
|
+
|
|
377
|
+
# # 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)
|
|
378
|
+
|
|
379
|
+
# # Key for high-confidence alerts (values 3, 6, 7, 8)
|
|
380
|
+
# high_conf_values = [3, 6, 7, 8]
|
|
381
|
+
# # where:
|
|
382
|
+
# # 3 = <50% loss, high confidence, ongoing
|
|
383
|
+
# # 6 = ≥50% loss, high confidence, ongoing
|
|
384
|
+
# # 7 = <50% loss, high confidence, finished
|
|
385
|
+
# # 8 = ≥50% loss, high confidence, finished
|
|
386
|
+
# # Note could use <50% loss (i.e. only 6 and 7) for if want to be more strict
|
|
387
|
+
|
|
388
|
+
# # Create high-confidence mask
|
|
389
|
+
# dist_high_conf = VEGDISTSTATUS.remap(
|
|
390
|
+
# high_conf_values, [1] * len(high_conf_values), 0
|
|
391
|
+
# )
|
|
392
|
+
|
|
393
|
+
# # Determine start year and current year dynamically
|
|
394
|
+
# start_year = 2024 # Set the first year of interest
|
|
395
|
+
# current_year = datetime.now().year
|
|
396
|
+
|
|
397
|
+
# # Calculate days since December 31, 2020 for start and end dates (server-side)
|
|
398
|
+
# start_of_2020 = ee.Date("2020-12-31").millis().divide(86400000).int()
|
|
399
|
+
|
|
400
|
+
# # Create a list to hold the yearly images
|
|
401
|
+
# yearly_images = []
|
|
402
|
+
|
|
403
|
+
# for year in range(start_year, current_year + 1):
|
|
404
|
+
# start_of_year = (
|
|
405
|
+
# ee.Date(f"{year}-01-01")
|
|
406
|
+
# .millis()
|
|
407
|
+
# .divide(86400000)
|
|
408
|
+
# .int()
|
|
409
|
+
# .subtract(start_of_2020)
|
|
410
|
+
# )
|
|
411
|
+
# start_of_next_year = (
|
|
412
|
+
# ee.Date(f"{year + 1}-01-01")
|
|
413
|
+
# .millis()
|
|
414
|
+
# .divide(86400000)
|
|
415
|
+
# .int()
|
|
416
|
+
# .subtract(start_of_2020)
|
|
417
|
+
# )
|
|
418
|
+
|
|
419
|
+
# # Filter VEG-DIST-DATE for the selected year
|
|
420
|
+
# dist_year = VEGDISTDATE.gte(start_of_year).And(
|
|
421
|
+
# VEGDISTDATE.lt(start_of_next_year)
|
|
422
|
+
# )
|
|
423
|
+
|
|
424
|
+
# # Apply high-confidence mask and rename the band
|
|
425
|
+
# high_conf_year = dist_year.updateMask(dist_high_conf).rename(
|
|
426
|
+
# f"DIST_year_{year}"
|
|
427
|
+
# )
|
|
428
|
+
|
|
429
|
+
# # Append the year's data to the list
|
|
430
|
+
# yearly_images.append(high_conf_year)
|
|
431
|
+
|
|
432
|
+
# # Combine all yearly images into a single image
|
|
433
|
+
# img_stack = ee.Image.cat(yearly_images)
|
|
434
|
+
|
|
435
|
+
# # Rename the bands correctly
|
|
436
|
+
# band_names = [f"DIST_year_{year}" for year in range(start_year, current_year + 1)]
|
|
437
|
+
# img_stack = img_stack.select(img_stack.bandNames(), band_names)
|
|
438
|
+
|
|
439
|
+
# return img_stack.updateMask(
|
|
440
|
+
# jrc_gfc_2020_prep()
|
|
441
|
+
# ) # mask yearly dist alerts to forest cover in 2020
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
#### disturbances combined (split into before and after 2020)
|
|
445
|
+
|
|
446
|
+
# RADD_after_2020
|
|
447
|
+
def radd_after_2020_prep():
|
|
448
|
+
from datetime import datetime
|
|
449
|
+
|
|
450
|
+
radd = ee.ImageCollection("projects/radar-wur/raddalert/v1")
|
|
451
|
+
|
|
452
|
+
radd_date = (
|
|
453
|
+
radd.filterMetadata("layer", "contains", "alert").select("Date").mosaic()
|
|
454
|
+
)
|
|
455
|
+
# date of avaialbility
|
|
456
|
+
start_year = 21 ## (starts 2019 in Africa, then 2020 for S America and Asia: https://data.globalforestwatch.org/datasets/gfw::deforestation-alerts-radd/about)
|
|
457
|
+
|
|
458
|
+
current_year = (
|
|
459
|
+
datetime.now().year % 100
|
|
460
|
+
) # NB the % 100 part gets last two digits needed
|
|
461
|
+
start = start_year * 1000
|
|
462
|
+
end = current_year * 1000 + 365
|
|
463
|
+
return (
|
|
464
|
+
radd_date.updateMask(radd_date.gte(start))
|
|
465
|
+
.updateMask(radd_date.lte(end))
|
|
466
|
+
.gt(0)
|
|
467
|
+
.rename("RADD_after_2020")
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
# RADD_before_2020
|
|
472
|
+
def radd_before_2020_prep():
|
|
473
|
+
from datetime import datetime
|
|
474
|
+
|
|
475
|
+
radd = ee.ImageCollection("projects/radar-wur/raddalert/v1")
|
|
476
|
+
|
|
477
|
+
radd_date = (
|
|
478
|
+
radd.filterMetadata("layer", "contains", "alert").select("Date").mosaic()
|
|
479
|
+
)
|
|
480
|
+
# date of avaialbility
|
|
481
|
+
start_year = 19 ## (starts 2019 in Africa, then 2020 for S America and Asia: https://data.globalforestwatch.org/datasets/gfw::deforestation-alerts-radd/about)
|
|
482
|
+
|
|
483
|
+
# current_year = datetime.now().year % 100 # NB the % 100 part gets last two digits needed
|
|
484
|
+
|
|
485
|
+
start = start_year * 1000
|
|
486
|
+
end = 20 * 1000 + 365
|
|
487
|
+
return (
|
|
488
|
+
radd_date.updateMask(radd_date.gte(start))
|
|
489
|
+
.updateMask(radd_date.lte(end))
|
|
490
|
+
.gt(0)
|
|
491
|
+
.rename("RADD_before_2020")
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
# # DIST_after_2020
|
|
496
|
+
# # alerts only for after 2020 currently so need to use date
|
|
497
|
+
# def glad_dist_after_2020_prep():
|
|
498
|
+
|
|
499
|
+
# # Load the vegetation disturbance collections
|
|
500
|
+
# VEGDISTSTATUS = ee.ImageCollection(
|
|
501
|
+
# "projects/glad/HLSDIST/current/VEG-DIST-STATUS"
|
|
502
|
+
# ).mosaic()
|
|
503
|
+
|
|
504
|
+
# # Key for high-confidence alerts (values 3, 6, 7, 8)
|
|
505
|
+
# high_conf_values = [3, 6, 7, 8]
|
|
506
|
+
|
|
507
|
+
# # Create high-confidence mask
|
|
508
|
+
# dist_high_conf = VEGDISTSTATUS.remap(
|
|
509
|
+
# high_conf_values, [1] * len(high_conf_values), 0
|
|
510
|
+
# )
|
|
511
|
+
|
|
512
|
+
# return dist_high_conf.updateMask(jrc_gfc_2020_prep()).rename(
|
|
513
|
+
# "DIST_after_2020"
|
|
514
|
+
# ) # Mask alerts to forest and rename band
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# TMF_deg_before_2020
|
|
518
|
+
def tmf_deg_before_2020_prep():
|
|
519
|
+
tmf_deg = ee.ImageCollection("projects/JRC/TMF/v1_2023/DegradationYear").mosaic()
|
|
520
|
+
return (tmf_deg.lte(2020)).And(tmf_deg.gte(2000)).rename("TMF_deg_before_2020")
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
# TMF_deg_after_2020
|
|
524
|
+
def tmf_deg_after_2020_prep():
|
|
525
|
+
tmf_deg = ee.ImageCollection("projects/JRC/TMF/v1_2023/DegradationYear").mosaic()
|
|
526
|
+
return tmf_deg.gt(2020).rename("TMF_deg_after_2020")
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
# tmf_def_before_2020
|
|
530
|
+
def tmf_def_before_2020_prep():
|
|
531
|
+
tmf_def = ee.ImageCollection("projects/JRC/TMF/v1_2023/DeforestationYear").mosaic()
|
|
532
|
+
return (tmf_def.lte(2020)).And(tmf_def.gte(2000)).rename("TMF_def_before_2020")
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
# tmf_def_after_2020
|
|
536
|
+
def tmf_def_after_2020_prep():
|
|
537
|
+
tmf_def = ee.ImageCollection("projects/JRC/TMF/v1_2023/DeforestationYear").mosaic()
|
|
538
|
+
return tmf_def.gt(2020).rename("TMF_def_after_2020")
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
# GFC_loss_before_2020 (loss within 10 percent cover; includes 2020; correct for version 11)
|
|
542
|
+
def glad_gfc_loss_before_2020_prep():
|
|
543
|
+
# Load the Global Forest Change dataset
|
|
544
|
+
gfc = ee.Image("UMD/hansen/global_forest_change_2023_v1_11")
|
|
545
|
+
gfc_loss = (
|
|
546
|
+
gfc.select(["lossyear"]).lte(20).And(gfc.select(["treecover2000"]).gt(10))
|
|
547
|
+
)
|
|
548
|
+
return gfc_loss.rename("GFC_loss_before_2020")
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
# GFC_loss_after_2020 (loss within 10 percent cover; correct for version 11)
|
|
552
|
+
def glad_gfc_loss_after_2020_prep():
|
|
553
|
+
# Load the Global Forest Change dataset
|
|
554
|
+
gfc = ee.Image("UMD/hansen/global_forest_change_2023_v1_11")
|
|
555
|
+
gfc_loss = gfc.select(["lossyear"]).gt(20).And(gfc.select(["treecover2000"]).gt(10))
|
|
556
|
+
return gfc_loss.rename("GFC_loss_after_2020")
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
# MODIS_fire_before_2020
|
|
560
|
+
def modis_fire_before_2020_prep():
|
|
561
|
+
modis_fire = ee.ImageCollection("MODIS/061/MCD64A1")
|
|
562
|
+
start_year = 2000
|
|
563
|
+
end_year = 2020
|
|
564
|
+
date_st = str(start_year) + "-01-01"
|
|
565
|
+
date_ed = str(end_year) + "-12-31"
|
|
566
|
+
return (
|
|
567
|
+
modis_fire.filterDate(date_st, date_ed)
|
|
568
|
+
.mosaic()
|
|
569
|
+
.select(["BurnDate"])
|
|
570
|
+
.gte(0)
|
|
571
|
+
.rename("MODIS_fire_before_2020")
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
# MODIS_fire_after_2020
|
|
576
|
+
def modis_fire_after_2020_prep():
|
|
577
|
+
modis_fire = ee.ImageCollection("MODIS/061/MCD64A1")
|
|
578
|
+
start_year = 2021
|
|
579
|
+
end_year = datetime.now().year
|
|
580
|
+
date_st = str(start_year) + "-01-01"
|
|
581
|
+
date_ed = str(end_year) + "-12-31"
|
|
582
|
+
return (
|
|
583
|
+
modis_fire.filterDate(date_st, date_ed)
|
|
584
|
+
.mosaic()
|
|
585
|
+
.select(["BurnDate"])
|
|
586
|
+
.gte(0)
|
|
587
|
+
.rename("MODIS_fire_after_2020")
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
# ESA_fire_before_2020
|
|
592
|
+
def esa_fire_before_2020_prep():
|
|
593
|
+
esa_fire = ee.ImageCollection("ESA/CCI/FireCCI/5_1")
|
|
594
|
+
start_year = 2000
|
|
595
|
+
end_year = 2020
|
|
596
|
+
date_st = str(start_year) + "-01-01"
|
|
597
|
+
date_ed = str(end_year) + "-12-31"
|
|
598
|
+
return (
|
|
599
|
+
esa_fire.filterDate(date_st, date_ed)
|
|
600
|
+
.mosaic()
|
|
601
|
+
.select(["BurnDate"])
|
|
602
|
+
.gte(0)
|
|
603
|
+
.rename("ESA_fire_before_2020")
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
# ###Combining datasets
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def combine_datasets():
|
|
611
|
+
"""Combines datasets into a single multiband image, with fallback if assets are missing."""
|
|
612
|
+
img_combined = ee.Image(1).rename(geometry_area_column)
|
|
613
|
+
|
|
614
|
+
# Combine images directly
|
|
615
|
+
for img in [func() for func in list_functions()]:
|
|
616
|
+
try:
|
|
617
|
+
img_combined = img_combined.addBands(img)
|
|
618
|
+
except ee.EEException as e:
|
|
619
|
+
# logger.error(f"Error adding image: {e}")
|
|
620
|
+
print(f"Error adding image: {e}")
|
|
621
|
+
|
|
622
|
+
try:
|
|
623
|
+
# Attempt to print band names to check for errors
|
|
624
|
+
print(img_combined.bandNames().getInfo())
|
|
625
|
+
except ee.EEException as e:
|
|
626
|
+
# logger.error(f"Error printing band names: {e}")
|
|
627
|
+
# logger.info("Running code for filtering to only valid datasets due to error in input")
|
|
628
|
+
print("using valid datasets filter due to error in input")
|
|
629
|
+
# Validate images
|
|
630
|
+
images_to_test = [func() for func in list_functions()]
|
|
631
|
+
valid_imgs = keep_valid_images(images_to_test) # Validate images
|
|
632
|
+
|
|
633
|
+
# Retry combining images after validation
|
|
634
|
+
img_combined = ee.Image(1).rename(geometry_area_column)
|
|
635
|
+
for img in valid_imgs:
|
|
636
|
+
img_combined = img_combined.addBands(img)
|
|
637
|
+
|
|
638
|
+
img_combined = img_combined.multiply(ee.Image.pixelArea())
|
|
639
|
+
|
|
640
|
+
return img_combined
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
######helper functions to check images
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
# list all functions ending with "_prep" (in the current script)
|
|
647
|
+
def list_functions():
|
|
648
|
+
# Use the module's globals to get all defined functions
|
|
649
|
+
current_module = inspect.getmodule(inspect.currentframe())
|
|
650
|
+
functions = [
|
|
651
|
+
func
|
|
652
|
+
for name, func in inspect.getmembers(current_module, inspect.isfunction)
|
|
653
|
+
if name.endswith("_prep")
|
|
654
|
+
]
|
|
655
|
+
return functions
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def keep_valid_images(images):
|
|
659
|
+
"""Keeps only valid images."""
|
|
660
|
+
valid_images = []
|
|
661
|
+
for img in images:
|
|
662
|
+
try:
|
|
663
|
+
img.getInfo() # This will raise an exception if the image is invalid
|
|
664
|
+
valid_images.append(img)
|
|
665
|
+
except ee.EEException as e:
|
|
666
|
+
# logger.error(f"Invalid image: {e}")
|
|
667
|
+
print(f"Invalid image: {e}")
|
|
668
|
+
return valid_images
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
# function to check if an image is valid
|
|
672
|
+
def ee_image_checker(image):
|
|
673
|
+
"""
|
|
674
|
+
Tests if the input is a valid ee.Image.
|
|
675
|
+
|
|
676
|
+
Args:
|
|
677
|
+
image: An ee.Image object.
|
|
678
|
+
|
|
679
|
+
Returns:
|
|
680
|
+
bool: True if the input is a valid ee.Image, False otherwise.
|
|
681
|
+
"""
|
|
682
|
+
try:
|
|
683
|
+
if ee.Algorithms.ObjectType(image).getInfo() == "Image":
|
|
684
|
+
# Trigger some action on the image to ensure it's a valid image
|
|
685
|
+
image.getInfo() # This will raise an exception if the image is invalid
|
|
686
|
+
return True
|
|
687
|
+
except ee.EEException as e:
|
|
688
|
+
print(f"Image validation failed with EEException: {e}")
|
|
689
|
+
except Exception as e:
|
|
690
|
+
print(f"Image validation failed with exception: {e}")
|
|
691
|
+
return False
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
# print(combine_valid_datasets().bandNames().getInfo())
|
|
695
|
+
# print(combine_datasets().bandNames().getInfo())
|