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