def restoration_carbon(rest_type, length_yr, crs, geojsons, EXECUTION_ID, logger): logger.debug("Entering restoration_carbon function.") # biomass agb_30m = ee.Image( "users/geflanddegradation/toolbox_datasets/forest_agb_30m_woodhole") agb_1km = ee.Image( "users/geflanddegradation/toolbox_datasets/forest_agb_1km_geocarbon") # c sequestration coefficients by intervention from winrock paper agfor0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b1").divide(100) agfor2060 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b2").divide(100) mshrr0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b3").divide(100) mshrr2060 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b4").divide(100) mtrer0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b5").divide(100) mtrer2060 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b6").divide(100) natre0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b7").divide(100) natre2060 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b8").divide(100) pwobr0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b9").divide(100) pweuc0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b10").divide(100) pwoak0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b11").divide(100) pwoco0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b12").divide(100) pwpin0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b13").divide(100) pwtea0020 = ee.Image( "users/geflanddegradation/toolbox_datasets/winrock_co2_seq_coeff_adm1" ).select("b14").divide(100) # combine the global 1km and the pantropical 30m datasets into one layer for analysis agb = agb_30m.float().unmask(0).where( ee.Image.pixelLonLat().select('latitude').abs().gte(29.999), agb_1km) # calculate below ground biomass following Mokany et al. 2006 = (0.489)*(AGB)^(0.89) bgb = agb.expression('0.489 * BIO**(0.89)', {'BIO': agb}) # calculate total biomass (t/ha) then convert to carbon equilavent (*0.5) to get Total Carbon (t ha-1) = (AGB+BGB)*0.5 tbc = agb.expression('(bgb + abg ) * 0.5 ', {'bgb': bgb, 'abg': agb}) # convert Total carbon to total CO2 eq (One ton of carbon equals 44/12 = 11/3 = 3.67 tons of carbon dioxide) current_co2 = tbc.expression('totalcarbon * 3.67 ', {'totalcarbon': tbc}) if rest_type == "terrestrial": if length_yr <= 20: d_natre = natre0020.multiply(length_yr).subtract(current_co2) d_natre = d_natre.where(d_natre.lte(0), 0) d_agfor = agfor0020.multiply(length_yr).subtract(current_co2) d_agfor = d_agfor.where(d_agfor.lte(0), 0) d_pwtea = pwtea0020.multiply(length_yr).subtract(current_co2) d_pweuc = pweuc0020.multiply(length_yr).subtract(current_co2) d_pwoak = pwoak0020.multiply(length_yr).subtract(current_co2) d_pwobr = pwobr0020.multiply(length_yr).subtract(current_co2) d_pwpin = pwpin0020.multiply(length_yr).subtract(current_co2) d_pwoco = pwoco0020.multiply(length_yr).subtract(current_co2) if length_yr > 20: d_natre = natre0020.multiply(20).add( natre2060.multiply(length_yr - 20)).subtract(current_co2) d_natre = d_natre.where(d_natre.lte(0), 0) d_agfor = agfor0020.multiply(20).add( agfor2060.multiply(length_yr - 20)).subtract(current_co2) d_agfor = d_agfor.where(d_agfor.lte(0), 0) d_pwtea = pwtea0020.multiply(20).subtract(current_co2) d_pweuc = pweuc0020.multiply(20).subtract(current_co2) d_pwoak = pwoak0020.multiply(20).subtract(current_co2) d_pwobr = pwobr0020.multiply(20).subtract(current_co2) d_pwpin = pwpin0020.multiply(20).subtract(current_co2) d_pwoco = pwoco0020.multiply(20).subtract(current_co2) output = current_co2.addBands(d_natre).addBands(d_agfor) \ .addBands(d_pwtea).addBands(d_pweuc) \ .addBands(d_pwoak).addBands(d_pwobr) \ .addBands(d_pwpin).addBands(d_pwoco) \ .rename(['current','natreg','agrfor','pwteak','pweuca','pwoaks','pwobro','pwpine','pwocon']) logger.debug("Setting up output for terrestrial restoration.") out = TEImage(output, [ BandInfo("Biomass (tonnes CO2e per ha)", add_to_map=True, metadata={'year': 'current'}), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'natural regeneration' }), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'agroforestry' }), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'teak plantation' }), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'eucalyptus plantation' }), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'oak plantation' }), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'other broadleaf plantation' }), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'pine plantation' }), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'conifer plantation' }) ]) elif rest_type == "coastal": if length_yr <= 20: d_mshrr = mshrr0020.multiply(length_yr).subtract(current_co2) d_mshrr = d_mshrr.where(d_mshrr.lte(0), 0) d_mtrer = mtrer0020.multiply(length_yr).subtract(current_co2) d_mtrer = d_mtrer.where(d_mtrer.lte(0), 0) if length_yr > 20: d_mshrr = mshrr0020.multiply(20).add( mshrr2060.multiply(length_yr - 20)).subtract(current_co2) d_mshrr = d_mshrr.where(d_mshrr.lte(0), 0) d_mtrer = mtrer0020.multiply(20).add( mtrer2060.multiply(length_yr - 20)).subtract(current_co2) d_mtrer = d_mtrer.where(d_mtrer.lte(0), 0) logger.debug("Setting up output for coastal restoration.") out = TEImage( current_co2.addBands(d_mshrr).addBands(d_mtrer).rename( ['current', 'mshrr', 'mtrer']), [ BandInfo("Biomass (tonnes CO2e per ha)", add_to_map=True, metadata={'year': 'current'}), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'mangrove shrub' }), BandInfo("Restoration biomass difference (tonnes CO2e per ha)", metadata={ 'years': length_yr, 'type': 'mangrove tree' }) ]) else: raise out.image = out.image.reproject(crs=agb_30m.projection()) return out
def soc(geometry, year_start, year_end, fl, remap_matrix, dl_annual_lc, EXECUTION_ID, logger): """ Calculate SOC indicator. """ logger.debug("Entering soc function.") geom = ee.Geometry.Polygon(geometry) # Location area = ee.FeatureCollection(geom) # soc soc = ee.Image( "users/geflanddegradation/toolbox_datasets/soc_sgrid_30cm").clip(area) soc_t0 = soc.updateMask(soc.neq(-32768)) # land cover - note it needs to be reprojected to match soc so that it can # be output to cloud storage in the same stack lc = ee.Image("users/geflanddegradation/toolbox_datasets/lcov_esacc_1992_2018") \ .select(ee.List.sequence(year_start - 1992, year_end - 1992, 1)) \ .clip(area) \ .reproject(crs=soc.projection()) lc = lc.where(lc.eq(9999), -32768) lc = lc.updateMask(lc.neq(-32768)) if fl == 'per pixel': # Setup a raster of climate regimes to use for coding Fl automatically climate = ee.Image("users/geflanddegradation/toolbox_datasets/ipcc_climate_zones") \ .clip(area) \ .remap([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [0, 2, 1, 2, 1, 2, 1, 2, 1, 5, 4, 4, 3]) clim_fl = climate.remap([0, 1, 2, 3, 4, 5], [0, 0.8, 0.69, 0.58, 0.48, 0.64]) # create empty stacks to store annual land cover maps stack_lc = ee.Image().select() # create empty stacks to store annual soc maps stack_soc = ee.Image().select() # loop through all the years in the period of analysis to compute changes in SOC for k in range(year_end - year_start): # land cover map reclassified to UNCCD 7 classes (1: forest, 2: # grassland, 3: cropland, 4: wetland, 5: artifitial, 6: bare, 7: water) lc_t0 = lc.select(k).remap(remap_matrix[0], remap_matrix[1]) lc_t1 = lc.select(k + 1).remap(remap_matrix[0], remap_matrix[1]) if (k == 0): # compute transition map (first digit for baseline land cover, and # second digit for target year land cover) lc_tr = lc_t0.multiply(10).add(lc_t1) # compute raster to register years since transition tr_time = ee.Image(2).where(lc_t0.neq(lc_t1), 1) else: # Update time since last transition. Add 1 if land cover remains # constant, and reset to 1 if land cover changed. tr_time = tr_time.where(lc_t0.eq(lc_t1), tr_time.add(ee.Image(1))) \ .where(lc_t0.neq(lc_t1), ee.Image(1)) # compute transition map (first digit for baseline land cover, and # second digit for target year land cover), but only update where # changes actually ocurred. lc_tr_temp = lc_t0.multiply(10).add(lc_t1) lc_tr = lc_tr.where(lc_t0.neq(lc_t1), lc_tr_temp) # stock change factor for land use - note the 99 and -99 will be # recoded using the chosen Fl option lc_tr_fl_0 = lc_tr.remap([ 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56, 57, 61, 62, 63, 64, 65, 66, 67, 71, 72, 73, 74, 75, 76, 77 ], [ 1, 1, 99, 1, 0.1, 0.1, 1, 1, 1, 99, 1, 0.1, 0.1, 1, -99, -99, 1, 1 / 0.71, 0.1, 0.1, 1, 1, 1, 0.71, 1, 0.1, 0.1, 1, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]) if fl == 'per pixel': lc_tr_fl = lc_tr_fl_0.where(lc_tr_fl_0.eq(99), clim_fl)\ .where(lc_tr_fl_0.eq(-99), ee.Image(1).divide(clim_fl)) else: lc_tr_fl = lc_tr_fl_0.where(lc_tr_fl_0.eq(99), fl)\ .where(lc_tr_fl_0.eq(-99), ee.Image(1).divide(fl)) # stock change factor for management regime lc_tr_fm = lc_tr.remap([ 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56, 57, 61, 62, 63, 64, 65, 66, 67, 71, 72, 73, 74, 75, 76, 77 ], [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]) # stock change factor for input of organic matter lc_tr_fo = lc_tr.remap([ 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56, 57, 61, 62, 63, 64, 65, 66, 67, 71, 72, 73, 74, 75, 76, 77 ], [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]) if (k == 0): soc_chg = (soc_t0.subtract((soc_t0.multiply(lc_tr_fl).multiply( lc_tr_fm).multiply(lc_tr_fo)))).divide(20) # compute final SOC stock for the period soc_t1 = soc_t0.subtract(soc_chg) # add to land cover and soc to stacks from both dates for the first # period stack_lc = stack_lc.addBands(lc_t0).addBands(lc_t1) stack_soc = stack_soc.addBands(soc_t0).addBands(soc_t1) else: # compute annual change in soc (updates from previous period based # on transition and time <20 years) soc_chg = soc_chg.where(lc_t0.neq(lc_t1), (stack_soc.select(k).subtract(stack_soc.select(k) \ .multiply(lc_tr_fl) \ .multiply(lc_tr_fm) \ .multiply(lc_tr_fo))).divide(20)) \ .where(tr_time.gt(20), 0) # compute final SOC for the period socn = stack_soc.select(k).subtract(soc_chg) # add land cover and soc to stacks only for the last year in the # period stack_lc = stack_lc.addBands(lc_t1) stack_soc = stack_soc.addBands(socn) # compute soc percent change for the analysis period soc_pch = ((stack_soc.select(year_end - year_start) \ .subtract(stack_soc.select(0))) \ .divide(stack_soc.select(0))) \ .multiply(100) logger.debug("Setting up output.") out = TEImage(soc_pch, [ BandInfo("Soil organic carbon (degradation)", add_to_map=True, metadata={ 'year_start': year_start, 'year_end': year_end }) ]) logger.debug("Adding annual SOC layers.") # Output all annual SOC layers d_soc = [] for year in range(year_start, year_end + 1): if (year == year_start) or (year == year_end): add_to_map = True else: add_to_map = False d_soc.append( BandInfo("Soil organic carbon", add_to_map=add_to_map, metadata={'year': year})) out.addBands(stack_soc, d_soc) if dl_annual_lc: logger.debug("Adding all annual LC layers.") d_lc = [] for year in range(year_start, year_end + 1): d_lc.append( BandInfo("Land cover (7 class)", metadata={'year': year})) out.addBands(stack_lc, d_lc) else: logger.debug("Adding initial and final LC layers.") out.addBands( stack_lc.select(0).addBands( stack_lc.select(len(stack_lc.getInfo()['bands']) - 1)), [ BandInfo("Land cover (7 class)", metadata={'year': year_start }), BandInfo("Land cover (7 class)", metadata={'year': year_end}) ]) out.image = out.image.unmask(-32768).int16() return out
def land_cover(year_baseline, year_target, trans_matrix, remap_matrix, EXECUTION_ID, logger): """ Calculate land cover indicator. """ logger.debug("Entering land_cover function.") ## land cover lc = ee.Image( "users/geflanddegradation/toolbox_datasets/lcov_esacc_1992_2018") lc = lc.where(lc.eq(9999), -32768) lc = lc.updateMask(lc.neq(-32768)) # Remap LC according to input matrix lc_remapped = lc.select('y{}'.format(year_baseline)).remap( remap_matrix[0], remap_matrix[1]) for year in range(year_baseline + 1, year_target + 1): lc_remapped = lc_remapped.addBands( lc.select('y{}'.format(year)).remap(remap_matrix[0], remap_matrix[1])) ## target land cover map reclassified to IPCC 6 classes lc_bl = lc_remapped.select(0) ## baseline land cover map reclassified to IPCC 6 classes lc_tg = lc_remapped.select(len(lc_remapped.getInfo()['bands']) - 1) ## compute transition map (first digit for baseline land cover, and second digit for target year land cover) lc_tr = lc_bl.multiply(10).add(lc_tg) ## definition of land cover transitions as degradation (-1), improvement (1), or no relevant change (0) lc_dg = lc_tr.remap([ 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56, 57, 61, 62, 63, 64, 65, 66, 67, 71, 72, 73, 74, 75, 76, 77 ], trans_matrix) ## Remap persistence classes so they are sequential. This ## makes it easier to assign a clear color ramp in QGIS. lc_tr = lc_tr.remap([ 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56, 57, 61, 62, 63, 64, 65, 66, 67, 71, 72, 73, 74, 75, 76, 77 ], [ 1, 12, 13, 14, 15, 16, 17, 21, 2, 23, 24, 25, 26, 27, 31, 32, 3, 34, 35, 36, 37, 41, 42, 43, 4, 45, 46, 47, 51, 52, 53, 54, 5, 56, 57, 61, 62, 63, 64, 65, 6, 67, 71, 72, 73, 74, 75, 76, 7 ]) logger.debug("Setting up output.") out = TEImage( lc_dg.addBands(lc.select('y{}'.format(year_baseline))).addBands( lc.select('y{}'.format(year_target))).addBands(lc_tr), [ BandInfo("Land cover (degradation)", add_to_map=True, metadata={ 'year_baseline': year_baseline, 'year_target': year_target }), BandInfo("Land cover (ESA classes)", metadata={'year': year_baseline}), BandInfo("Land cover (ESA classes)", metadata={'year': year_target}), BandInfo("Land cover transitions", add_to_map=True, metadata={ 'year_baseline': year_baseline, 'year_target': year_target }) ]) # Return the full land cover timeseries so it is available for reporting logger.debug("Adding annual lc layers.") d_lc = [] for year in range(year_baseline, year_target + 1): if (year == year_baseline) or (year == year_target): add_to_map = True else: add_to_map = False d_lc.append( BandInfo("Land cover (7 class)", add_to_map=add_to_map, metadata={'year': year})) out.addBands(lc_remapped, d_lc) out.image = out.image.unmask(-32768).int16() return out