def process_map(lookupobj, csvfilename):
    """Produce a CSV file of areas per country from a dataset."""
    shapefilename = 'data/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp'
    df = pd.DataFrame(columns=lookupobj.get_columns(), dtype=float)
    df.index.name = 'Country'
    shapefile = osgeo.ogr.Open(shapefilename)
    assert shapefile.GetLayerCount() == 1
    layer = shapefile.GetLayerByIndex(0)

    for idx, feature in enumerate(layer):
        admin = admin_names.lookup(feature.GetField("ADMIN"))
        if admin is None:
            continue
        a3 = feature.GetField("SOV_A3")
        if admin not in df.index:
            df.loc[admin] = [0] * len(df.columns)

        print(f"Processing {admin:<41} #{a3}_{idx}")
        maskfilename = f"masks/{a3}_{idx}_{lookupobj.maskdim}_mask._tif"
        maskimg = osgeo.gdal.Open(maskfilename, osgeo.gdal.GA_ReadOnly)
        maskband = maskimg.GetRasterBand(1)
        x_siz = maskband.XSize
        y_siz = maskband.YSize
        x_blksiz, y_blksiz = maskband.GetBlockSize()
        for y in range(0, y_siz, y_blksiz):
            nrows = geoutil.blklim(coord=y, blksiz=y_blksiz, totsiz=y_siz)
            for x in range(0, x_siz, x_blksiz):
                ncols = geoutil.blklim(coord=x, blksiz=x_blksiz, totsiz=x_siz)
                if geoutil.is_sparse(band=maskband,
                                     x=x,
                                     y=y,
                                     ncols=ncols,
                                     nrows=nrows):
                    # sparse hole in image, no data to process
                    continue

                maskblock = maskband.ReadAsArray(x, y, ncols, nrows)
                km2block = geoutil.km2_block(nrows=nrows,
                                             ncols=ncols,
                                             y_off=y,
                                             img=maskimg)
                lookupobj.km2(x=x,
                              y=y,
                              ncols=ncols,
                              nrows=nrows,
                              maskblock=maskblock,
                              km2block=km2block,
                              df=df,
                              admin=admin)
    outputfilename = os.path.join('results', csvfilename)
    df.sort_index(axis='index').to_csv(outputfilename, float_format='%.2f')
    return df
def produce_GeoTIFF():
    """Produce a GeoTIFF file of Thermal Moisture Regime + Agro-Ecological Zone."""
    kg_filename = 'data/Beck_KG_V1/Beck_KG_V1_present_0p0083.tif'
    lc_filename = 'data/copernicus/C3S-LC-L4-LCCS-Map-300m-P1Y-2018-v2.1.1.tif'
    sl_filename = 'data/ConsolidatedSlope.tif'
    wk_filename = 'data/FAO/workability_FAO_sq7_1km.tif'
    kg_img = osgeo.gdal.Open(kg_filename, osgeo.gdal.GA_ReadOnly)
    kg_band = kg_img.GetRasterBand(1)
    lc_img = osgeo.gdal.Open(lc_filename, osgeo.gdal.GA_ReadOnly)
    lc_band = lc_img.GetRasterBand(1)
    sl_img = osgeo.gdal.Open(sl_filename, osgeo.gdal.GA_ReadOnly)
    sl_band = {}
    for idx in range(1, 9):
        sl_band[idx] = sl_img.GetRasterBand(idx)
    wk_img = osgeo.gdal.Open(wk_filename, osgeo.gdal.GA_ReadOnly)
    wk_band = wk_img.GetRasterBand(1)

    aez_f = create_AEZ_GeoTIFF(ref_img=lc_img, filename='results/AEZ.tif')
    slope_f = create_slope_GeoTIFF(ref_img=lc_img,
                                   filename='results/Slope.tif')
    land_use_f = create_land_use_GeoTIFF(ref_img=lc_img,
                                         filename='results/LandUse.tif')
    soil_health_f = create_soil_health_GeoTIFF(
        ref_img=lc_img, filename='results/SoilHealth.tif')

    x_siz = lc_band.XSize
    y_siz = lc_band.YSize
    x_blksiz, y_blksiz = (768, 768)

    for y in range(0, y_siz, y_blksiz):
        print('.', end='', flush=True)
        nrows = geoutil.blklim(coord=y, blksiz=y_blksiz, totsiz=y_siz)
        for x in range(0, x_siz, x_blksiz):
            ncols = geoutil.blklim(coord=x, blksiz=x_blksiz, totsiz=x_siz)

            x3 = int(x / 3)
            y3 = int(y / 3)
            ncols3 = int(ncols / 3)
            nrows3 = int(nrows / 3)

            k = kg_band.ReadAsArray(x3, y3, ncols3, nrows3)
            kg_blk = np.repeat(np.repeat(k, 3, axis=1), 3, axis=0)
            regime = populate_tmr(kg_blk)

            sl_blk = {}
            for idx in range(1, 9):
                s = sl_band[idx].ReadAsArray(x3, y3, ncols3, nrows3)
                sl_blk[idx] = np.repeat(np.repeat(s, 3, axis=1), 3, axis=0)

            slope = populate_slope(sl_blk)
            plurality = {}
            plurality['steep'] = ((slope['steep'] >= slope['moderate']) &
                                  (slope['steep'] >= slope['minimal']))
            plurality['moderate'] = ((slope['moderate'] > slope['steep']) &
                                     (slope['moderate'] >= slope['minimal']))
            plurality['minimal'] = ((slope['minimal'] > slope['steep']) &
                                    (slope['minimal'] >= slope['moderate']))
            slope = plurality

            lc_blk = lc_band.ReadAsArray(x, y, ncols, nrows)
            land_use = populate_land_use(lc_blk)

            k = wk_band.ReadAsArray(x3, y3, ncols3, nrows3)
            wk_blk = np.repeat(np.repeat(k, 3, axis=1), 3, axis=0)
            soil_health = populate_soil_health(wk_blk)

            outarray = np.full((nrows, ncols), C_TMR_BLNK)
            for tmr, color in tmr_state.items():
                for aez in yield_AEZs(regime=regime,
                                      tmr=tmr,
                                      slope=slope,
                                      land_use=land_use,
                                      soil_health=soil_health):
                    outarray[aez.astype(bool)] = color
                    color += 1
            aez_f.GetRasterBand(1).WriteArray(outarray, xoff=x, yoff=y)

            outarray = np.full((nrows, ncols), C_SLP_BLNK)
            outarray[slope['minimal'].astype(bool)] = C_SLP_MIN
            outarray[slope['moderate'].astype(bool)] = C_SLP_MOD
            outarray[slope['steep'].astype(bool)] = C_SLP_STP
            slope_f.GetRasterBand(1).WriteArray(outarray, xoff=x, yoff=y)

            outarray = np.full((nrows, ncols), C_LUS_BLNK)
            outarray[land_use['forest'].astype(bool)] = C_LUS_FRST
            outarray[land_use['cropland_rainfed'].astype(bool)] = C_LUS_CRRF
            outarray[land_use['cropland_irrigated'].astype(bool)] = C_LUS_CRIR
            outarray[land_use['grassland'].astype(bool)] = C_LUS_GRSS
            outarray[land_use['bare'].astype(bool)] = C_LUS_BARE
            outarray[land_use['urban'].astype(bool)] = C_LUS_URBN
            outarray[land_use['water'].astype(bool)] = C_LUS_WATR
            outarray[land_use['ice'].astype(bool)] = C_LUS_ICE
            land_use_f.GetRasterBand(1).WriteArray(outarray, xoff=x, yoff=y)

            outarray = np.full((nrows, ncols), C_SLP_BLNK)
            outarray[soil_health['prime'].astype(bool)] = C_SLH_GOOD
            outarray[soil_health['good'].astype(bool)] = C_SLH_MRGN
            outarray[soil_health['marginal'].astype(bool)] = C_SLH_POOR
            outarray[soil_health['barren'].astype(bool)] = C_SLH_BARE
            outarray[soil_health['water'].astype(bool)] = C_SLH_WATR
            soil_health_f.GetRasterBand(1).WriteArray(outarray, xoff=x, yoff=y)

    aez_f = None
    slope_f = None
    land_use_f = None
    soil_health_f = None
def produce_CSV():
    """Produce a CSV file of Thermal Moisture Regime + Agro-Ecological Zone per country."""
    columns = []
    for tmr in tmr_state.keys():
        columns.extend([f"{tmr}|AEZ{x}" for x in range(1, 30)])
    df = pd.DataFrame(columns=columns, dtype='float')
    df.index.name = 'Country'

    countrycsvfilename = 'results/AEZ-by-country.csv'
    shapefilename = 'data/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp'
    kg_filename = 'data/Beck_KG_V1/Beck_KG_V1_present_0p0083.tif'
    lc_filename = 'data/copernicus/C3S-LC-L4-LCCS-Map-300m-P1Y-2018-v2.1.1.tif'
    sl_filename = 'data/ConsolidatedSlope.tif'
    wk_filename = 'data/FAO/workability_FAO_sq7_1km.tif'
    shapefile = osgeo.ogr.Open(shapefilename)
    assert shapefile.GetLayerCount() == 1
    features = shapefile.GetLayerByIndex(0)
    kg_img = osgeo.gdal.Open(kg_filename, osgeo.gdal.GA_ReadOnly)
    kg_band = kg_img.GetRasterBand(1)
    lc_img = osgeo.gdal.Open(lc_filename, osgeo.gdal.GA_ReadOnly)
    lc_band = lc_img.GetRasterBand(1)
    sl_img = osgeo.gdal.Open(sl_filename, osgeo.gdal.GA_ReadOnly)
    sl_band = {}
    for idx in range(1, 9):
        sl_band[idx] = sl_img.GetRasterBand(idx)
    wk_img = osgeo.gdal.Open(wk_filename, osgeo.gdal.GA_ReadOnly)
    wk_band = wk_img.GetRasterBand(1)

    for idx, feature in enumerate(features):
        admin = admin_names.lookup(feature.GetField("ADMIN"))
        if admin is None:
            continue
        a3 = feature.GetField("SOV_A3")
        if admin not in df.index:
            df.loc[admin] = [0] * len(df.columns)

        print(f"Processing {admin:<41} #{a3}_{idx}")
        maskfilename = f"masks/{a3}_{idx}_1km_mask._tif"
        maskimg = osgeo.gdal.Open(maskfilename, osgeo.gdal.GA_ReadOnly)
        mask_band = maskimg.GetRasterBand(1)
        x_siz = mask_band.XSize
        y_siz = mask_band.YSize
        x_blksiz, y_blksiz = mask_band.GetBlockSize()
        for y in range(0, y_siz, y_blksiz):
            nrows = geoutil.blklim(coord=y, blksiz=y_blksiz, totsiz=y_siz)
            for x in range(0, x_siz, x_blksiz):
                ncols = geoutil.blklim(coord=x, blksiz=x_blksiz, totsiz=x_siz)
                if geoutil.is_sparse(band=mask_band,
                                     x=x,
                                     y=y,
                                     ncols=ncols,
                                     nrows=nrows):
                    # sparse hole in image, no data to process
                    continue

                mask_blk = mask_band.ReadAsArray(x, y, ncols, nrows)
                k = geoutil.km2_block(nrows=nrows,
                                      ncols=ncols,
                                      y_off=y,
                                      img=maskimg)
                k[np.logical_not(mask_blk)] = 0.0
                km2_blk = (np.repeat(np.repeat(k, 3, axis=1), 3, axis=0)) / 9.0

                k = kg_band.ReadAsArray(x, y, ncols, nrows)
                kg_blk = np.repeat(np.repeat(k, 3, axis=1), 3, axis=0)
                regime = populate_tmr(kg_blk)

                sl_blk = {}
                for idx in range(1, 9):
                    s = sl_band[idx].ReadAsArray(x, y, ncols, nrows)
                    sl_blk[idx] = np.repeat(np.repeat(s, 3, axis=1), 3, axis=0)
                slope = populate_slope(sl_blk)

                lc_blk = lc_band.ReadAsArray(3 * x, 3 * y, 3 * ncols,
                                             3 * nrows)
                land_use = populate_land_use(lc_blk)

                w = wk_band.ReadAsArray(x, y, ncols, nrows)
                wk_blk = np.repeat(np.repeat(w, 3, axis=1), 3, axis=0)
                soil_health = populate_soil_health(wk_blk)

                for tmr in tmr_state.keys():
                    n = 1
                    for aez in yield_AEZs(regime=regime,
                                          tmr=tmr,
                                          slope=slope,
                                          land_use=land_use,
                                          soil_health=soil_health):
                        df.loc[admin, f"{tmr}|AEZ{n}"] += (aez * km2_blk).sum()
                        n += 1

    df.sort_index(axis='index').to_csv(countrycsvfilename, float_format='%.2f')

    regions = [
        'OECD90', 'Eastern Europe', 'Asia (Sans Japan)',
        'Middle East and Africa', 'Latin America', 'China', 'India', 'EU',
        'USA'
    ]
    df_region = pd.DataFrame(0, index=regions, columns=df.columns.copy())
    df_region.index.name = 'Region'
    for country, row in df.iterrows():
        region = admin_names.region_mapping[country]
        if region is not None:
            df_region.loc[region, :] += row

    for tmr in [
            'Tropical-Humid', 'Arid', 'Tropical-Semiarid', 'Temperate-Humid',
            'Temperate-Semiarid', 'Boreal-Humid', 'Boreal-Semiarid', 'Arctic'
    ]:
        tmrfilename = tmr.translate(str.maketrans('/', '-'))
        filename = f"results/AEZ-{tmrfilename}-by-region.csv"
        df_region.filter(regex=f'^{tmr.lower()}',
                         axis=1).to_csv(filename, float_format='%.2f')
def test_blklim():
    assert geoutil.blklim(coord=0, blksiz=256, totsiz=1024) == 256
    assert geoutil.blklim(coord=768, blksiz=256, totsiz=1024) == 256
    assert geoutil.blklim(coord=900, blksiz=256, totsiz=1024) == 124
def produce_CSV():
    """Produce a CSV file of degraded land for {forest, cropland, grassland}."""
    columns = [
        'forest:good:degraded',
        'forest:marginal:degraded',
        'forest:poor:degraded',
        'forest:verypoor:degraded',
        'forest:good:nondegraded',
        'forest:marginal:nondegraded',
        'forest:poor:nondegraded',
        'forest:verypoor:nondegraded',
        'cropland:good:degraded',
        'cropland:marginal:degraded',
        'cropland:poor:degraded',
        'cropland:verypoor:degraded',
        'cropland:good:nondegraded',
        'cropland:marginal:nondegraded',
        'cropland:poor:nondegraded',
        'cropland:verypoor:nondegraded',
        'grassland:good:degraded',
        'grassland:marginal:degraded',
        'grassland:poor:degraded',
        'grassland:verypoor:degraded',
        'grassland:good:nondegraded',
        'grassland:marginal:nondegraded',
        'grassland:poor:nondegraded',
        'grassland:verypoor:nondegraded',
        'bare:good:degraded',
        'bare:marginal:degraded',
        'bare:poor:degraded',
        'bare:verypoor:degraded',
        'bare:good:nondegraded',
        'bare:marginal:nondegraded',
        'bare:poor:nondegraded',
        'bare:verypoor:nondegraded',
        'urban:good:degraded',
        'urban:marginal:degraded',
        'urban:poor:degraded',
        'urban:verypoor:degraded',
        'urban:good:nondegraded',
        'urban:marginal:nondegraded',
        'urban:poor:nondegraded',
        'urban:verypoor:nondegraded',
        'water:good:degraded',
        'water:marginal:degraded',
        'water:poor:degraded',
        'water:verypoor:degraded',
        'water:good:nondegraded',
        'water:marginal:nondegraded',
        'water:poor:nondegraded',
        'water:verypoor:nondegraded',
        'ice:good:degraded',
        'ice:marginal:degraded',
        'ice:poor:degraded',
        'ice:verypoor:degraded',
        'ice:good:nondegraded',
        'ice:marginal:nondegraded',
        'ice:poor:nondegraded',
        'ice:verypoor:nondegraded',
    ]
    df = pd.DataFrame(columns=columns, dtype='float')
    df.index.name = 'Country'

    shapefilename = 'data/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp'
    shapefile = osgeo.ogr.Open(shapefilename)
    assert shapefile.GetLayerCount() == 1
    features = shapefile.GetLayerByIndex(0)
    lc_filename = 'data/copernicus/ESACCI-LC-L4-LCCS-Map-300m-P1Y-2015-v2.0.7.tif'
    lc_img = osgeo.gdal.Open(lc_filename, osgeo.gdal.GA_ReadOnly)
    lc_band = lc_img.GetRasterBand(1)

    lpd_filename = 'data/lpd_int2/lpd_int2.tif'
    lpd_img = osgeo.gdal.Open(lpd_filename, osgeo.gdal.GA_ReadOnly)
    lpd_band = lpd_img.GetRasterBand(1)

    wk_filename = 'data/FAO/workability_FAO_sq7_1km.tif'
    wk_img = osgeo.gdal.Open(wk_filename, osgeo.gdal.GA_ReadOnly)
    wk_band = wk_img.GetRasterBand(1)

    for idx, feature in enumerate(features):
        admin = admin_names.lookup(feature.GetField("ADMIN"))
        if admin is None:
            continue
        a3 = feature.GetField("SOV_A3")
        if admin not in df.index:
            df.loc[admin] = [0] * len(df.columns)

        print(f"Processing {admin:<41} #{a3}_{idx}")
        maskfilename = f"masks/{a3}_{idx}_1km_mask._tif"
        maskimg = osgeo.gdal.Open(maskfilename, osgeo.gdal.GA_ReadOnly)
        mask_band = maskimg.GetRasterBand(1)
        x_siz = mask_band.XSize
        y_siz = mask_band.YSize
        x_blksiz, y_blksiz = mask_band.GetBlockSize()
        for y in range(0, y_siz, y_blksiz):
            nrows = geoutil.blklim(coord=y, blksiz=y_blksiz, totsiz=y_siz)
            for x in range(0, x_siz, x_blksiz):
                ncols = geoutil.blklim(coord=x, blksiz=x_blksiz, totsiz=x_siz)
                if geoutil.is_sparse(band=mask_band,
                                     x=x,
                                     y=y,
                                     ncols=ncols,
                                     nrows=nrows):
                    # sparse hole in image, no data to process
                    continue

                mask_blk = mask_band.ReadAsArray(x, y, ncols, nrows)
                k = geoutil.km2_block(nrows=nrows,
                                      ncols=ncols,
                                      y_off=y,
                                      img=maskimg)
                k[np.logical_not(mask_blk)] = 0.0
                km2_blk = (np.repeat(np.repeat(k, 3, axis=1), 3, axis=0)) / 9.0

                lc_blk = lc_band.ReadAsArray(3 * x, 3 * y, 3 * ncols,
                                             3 * nrows)
                lc = {}
                lc['forest'] = np.logical_or.reduce(
                    (lc_blk == 12, lc_blk == 50, lc_blk == 60, lc_blk == 61,
                     lc_blk == 62, lc_blk == 70, lc_blk == 71, lc_blk == 72,
                     lc_blk == 80, lc_blk == 81, lc_blk == 82, lc_blk == 90,
                     lc_blk == 160, lc_blk == 170))
                lc['cropland'] = np.logical_or.reduce(
                    (lc_blk == 10, lc_blk == 30, lc_blk == 20))
                lc['grassland'] = np.logical_or.reduce(
                    (lc_blk == 11, lc_blk == 40, lc_blk == 100, lc_blk == 110,
                     lc_blk == 120, lc_blk == 121, lc_blk == 122,
                     lc_blk == 130, lc_blk == 150, lc_blk == 151,
                     lc_blk == 152, lc_blk == 153, lc_blk == 180))
                lc['bare'] = np.logical_or.reduce(
                    (lc_blk == 140, lc_blk == 200, lc_blk == 201,
                     lc_blk == 202))
                lc['urban'] = (lc_blk == 190)
                lc['water'] = (lc_blk == 210)
                lc['ice'] = (lc_blk == 220)

                k = lpd_band.ReadAsArray(x, y, ncols, nrows)
                lpd = {}
                lpd_blk = np.repeat(np.repeat(k, 3, axis=1), 3, axis=0)
                lpd['degraded'] = (lpd_blk != 0.0)
                lpd['nondegraded'] = (lpd_blk == 0.0)

                k = wk_band.ReadAsArray(x, y, ncols, nrows)
                wk_blk = np.repeat(np.repeat(k, 3, axis=1), 3, axis=0)
                work = {}
                work['good'] = (wk_blk == 1)
                work['marginal'] = (wk_blk == 2)
                work['poor'] = (wk_blk == 3)
                work['verypoor'] = (wk_blk == 4)

                for cover in lc.keys():
                    for degraded in lpd.keys():
                        for soil in work.keys():
                            key = f'{cover}:{soil}:{degraded}'
                            df.loc[admin, key] += (np.logical_and.reduce(
                                (lc[cover], lpd[degraded], work[soil])) *
                                                   km2_blk).sum()

    csvfilename = 'results/degraded-cover-by-country.csv'
    df.sort_index(axis='index').to_csv(csvfilename, float_format='%.2f')

    regions = [
        'OECD90', 'Eastern Europe', 'Asia (Sans Japan)',
        'Middle East and Africa', 'Latin America', 'China', 'India', 'EU',
        'USA'
    ]
    df_region = pd.DataFrame(0, index=regions, columns=df.columns.copy())
    df_region.index.name = 'Region'
    for country, row in df.iterrows():
        region = admin_names.region_mapping[country]
        if region is not None:
            df_region.loc[region, :] += row
    csvfilename = f"results/degraded-cover-by-region.csv"
    df_region.to_csv(csvfilename, float_format='%.2f')