Exemplo n.º 1
0
def img_layerstack(sensor='LS',
                   bands=None,
                   years=None,
                   months=None,
                   pixel_resolution=None,
                   cloud_cover=70,
                   masks=None,
                   roi=None,
                   epsg=None,
                   exclude_slc_off=False,
                   export_option="Drive",
                   asset_path=None,
                   export_name=None,
                   lst_threshold=None,
                   wv_method="NCEP"):

    if roi is None:
        roi = [13.08, 52.32, 13.76, 52.67]  # Berlin
        print("No region of interest specified. Berlin it is.")
    if masks is None:
        masks = ['cloud', 'cshadow', 'snow']
        print("No masks specified. Masking clouds, cloud shadows and snow.")
    if pixel_resolution is None:
        pixel_resolution = 30
    if sensor is None:
        sensor = "LS"
        print("No sensor specified. Landsat it is.")
    if bands is None:
        bands = ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']
        print("No bands specified. B-G-R-NIR-SWIR1-SWIR2 it is.")
    if (export_option == "Asset") & (asset_path is None):
        print("No asset path specified.")
        return
    if export_name is None:
        export_name = "BERLIN"
    # roi client to server
    roi_geom = ee.Geometry.Rectangle(roi)

    # find epsg
    if epsg is None:
        epsg = generals.find_utm(roi_geom)

    # time
    years = [years] if isinstance(years, int) else years
    months = [months] if isinstance(months, int) else months
    time_filter = generals.time_filter(l_years=years, l_months=months)

    # image collections
    imgCol_L5_SR = ee.ImageCollection('LANDSAT/LT05/C01/T1_SR') \
        .filterBounds(roi_geom) \
        .filter(time_filter) \
        .filter(ee.Filter.lt('CLOUD_COVER_LAND', cloud_cover)) \
        .map(prepro.rename_bands_l5) \
        .map(prepro.mask_landsat_sr(masks)) \
        .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'], ['TIR'])) \
        .map(prepro.scale_img(0.1, ['TIR'], ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']))

    imgCol_L7_SR = ee.ImageCollection('LANDSAT/LE07/C01/T1_SR') \
        .filterBounds(roi_geom) \
        .filter(time_filter) \
        .filter(ee.Filter.lt('CLOUD_COVER_LAND', cloud_cover)) \
        .map(prepro.rename_bands_l7) \
        .map(prepro.mask_landsat_sr(masks)) \
        .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'], ['TIR'])) \
        .map(prepro.scale_img(0.1, ['TIR'], ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']))

    # check SLC_OFF statement
    if exclude_slc_off:
        imgCol_L7_SR = imgCol_L7_SR.filter(
            ee.Filter.date("1999-04-18", "2003-05-31"))

    imgCol_L8_SR = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR') \
        .filterBounds(roi_geom) \
        .filter(time_filter) \
        .filter(ee.Filter.lt('CLOUD_COVER_LAND', cloud_cover)) \
        .map(prepro.rename_bands_l8) \
        .map(prepro.mask_landsat_sr(masks)) \
        .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'], ['TIR'])) \
        .map(prepro.scale_img(0.1, ['TIR'], ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']))

    imgCol_S2_L1C = ee.ImageCollection('COPERNICUS/S2') \
        .filterBounds(roi_geom) \
        .filter(time_filter) \
        .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', cloud_cover)) \
        .map(prepro.mask_s2_cdi(-0.5)) \
        .map(prepro.rename_bands_s2) \
        .map(prepro.mask_s2) \
        .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']))

    imgCol_S2_L2A = ee.ImageCollection('COPERNICUS/S2_SR') \
        .filterBounds(roi_geom) \
        .filter(time_filter) \
        .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', cloud_cover)) \
        .map(prepro.mask_s2_cdi(-0.5)) \
        .map(prepro.rename_bands_s2) \
        .map(prepro.mask_s2_scl) \
        .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']))

    if 'LST' in bands:
        if imgCol_L5_SR.size().getInfo() > 0:
            imgCol_L5_SR = lst.apply_lst_prepro(imgCol_L5_SR,
                                                sensor="L5",
                                                time_filter=time_filter,
                                                roi=roi_geom,
                                                cloud_cover=cloud_cover,
                                                wv_method=wv_method)
        if imgCol_L7_SR.size().getInfo() > 0:
            imgCol_L7_SR = lst.apply_lst_prepro(imgCol_L7_SR,
                                                sensor="L7",
                                                time_filter=time_filter,
                                                roi=roi_geom,
                                                cloud_cover=cloud_cover,
                                                wv_method=wv_method)
        if imgCol_L8_SR.size().getInfo() > 0:
            imgCol_L8_SR = lst.apply_lst_prepro(imgCol_L8_SR,
                                                sensor="L8",
                                                time_filter=time_filter,
                                                roi=roi_geom,
                                                cloud_cover=cloud_cover,
                                                wv_method=wv_method)

    if 'ALBEDO' in bands:
        imgCol_L5_SR = imgCol_L5_SR.map(prepro.surface_albedo(sensor="L5"))
        imgCol_L7_SR = imgCol_L7_SR.map(prepro.surface_albedo(sensor="L7"))
        imgCol_L8_SR = imgCol_L8_SR.map(prepro.surface_albedo(sensor="L8"))

    # --------------------------------------------------
    # MERGE imgCols
    # --------------------------------------------------
    if sensor == 'S2_L1C':
        imgCol_SR = imgCol_S2_L1C
    elif sensor == 'S2_L2A':
        imgCol_SR = imgCol_S2_L2A
    elif sensor == 'LS':
        imgCol_SR = imgCol_L5_SR.merge(imgCol_L7_SR).merge(imgCol_L8_SR)
    elif sensor == 'L8':
        imgCol_SR = imgCol_L8_SR
    elif sensor == 'L7':
        imgCol_SR = imgCol_L7_SR
    elif sensor == 'L5':
        imgCol_SR = imgCol_L5_SR
    elif sensor == 'SL8':
        imgCol_SR = imgCol_L8_SR.merge(imgCol_S2_L2A)
    elif sensor == 'SL':
        imgCol_SR = imgCol_L5_SR.merge(imgCol_L7_SR).merge(imgCol_L8_SR).merge(
            imgCol_S2_L2A)
    else:
        imgCol_SR = None
        print('No sensor specified!')

    imgCol_SR = imgCol_SR.sort("system:time_start")

    # --------------------------------------------------
    # Calculate Indices
    # --------------------------------------------------
    if ('NDVI' in bands) or ('LST' in bands):
        imgCol_SR = imgCol_SR.map(prepro.ndvi)
    if 'EVI' in bands:
        imgCol_SR = imgCol_SR.map(prepro.evi())
    if 'NDWI1' in bands:
        imgCol_SR = imgCol_SR.map(prepro.ndwi1)
    if ('NDWI2' in bands) or ('LST' in bands):
        imgCol_SR = imgCol_SR.map(prepro.ndwi2)
    if 'NDBI' in bands:
        imgCol_SR = imgCol_SR.map(prepro.ndbi)
    if 'TCG' in bands:
        imgCol_SR = imgCol_SR.map(prepro.tcg)
    if 'TCB' in bands:
        imgCol_SR = imgCol_SR.map(prepro.tcb)
    if 'TCW' in bands:
        imgCol_SR = imgCol_SR.map(prepro.tcw)

    if 'LST' in bands:
        imgCol_SR = imgCol_SR.map(
            prepro.fvc(ndvi_soil=0.15, ndvi_vegetation=0.9))
        imgCol_SR = imgCol_SR.map(lst.emissivity())
        imgCol_SR = imgCol_SR.map(lst.land_surface_temperature(scale=0.01))
        if lst_threshold:
            imgCol_SR = imgCol_SR.map(
                lst.mask_lst(threshold=lst_threshold, scale=0.01))

    dates = get_dates(imgCol_SR)
    date_range = dates.distinct()
    newcol = mosaic(imgCol_SR.select(bands), date_range)

    for band in bands:
        lyr = layerstack(newcol.select(band))
        lyr = lyr.multiply(10000)
        lyr = lyr.toInt16()

        out_file = sensor + '_layerstack_' + export_name + '_' + band + '_' + str(min(years)) + '-' + str(max(years))+ \
                   '_' + str(min(months)) + '-' + str(max(months))

        # export image
        if export_option == "Drive":
            out = ee.batch.Export.image.toDrive(
                image=lyr,
                description=out_file,
                scale=pixel_resolution,
                maxPixels=1e13,
                region=roi_geom['coordinates'][0],
                crs=epsg)
        elif export_option == "Asset":
            out = ee.batch.Export.image.toAsset(
                image=lyr,
                description=out_file,
                assetId=asset_path + out_file,
                scale=pixel_resolution,
                maxPixels=1e13,
                region=roi_geom['coordinates'][0],
                crs=epsg)
        else:
            print(
                "Invalid export option specified. Must be one of 'Drive' or 'Asset'"
            )

        process = ee.batch.Task.start(out)

    return print("Submitted to Server.")
Exemplo n.º 2
0
# ====================================================================================================#
# select bits for mask
'''
dict_mask = {'cloud': ee.Number(2).pow(5).int(),
             'cshadow': ee.Number(2).pow(3).int(),
             'snow': ee.Number(2).pow(4).int()}

sel_masks = [dict_mask[x] for x in MASKS]
bits = ee.Number(1)

for m in sel_masks:
    bits = ee.Number(bits.add(m))
'''
# find epsg
if EPSG == 'UTM':
    EPSG = generals.find_utm(ROI)

for year in TARGET_YEARS:
    for i in range(len(TARGET_DOYS)):

        # time
        iter_target_doy = TARGET_DOYS[i]

        year_min = year - SURR_YEARS
        year_max = year + SURR_YEARS

        temp_filter = []
        for t in range(year_min, year_max + 1):
            temp_target_doy = datetime.datetime(
                t, 1, 1) + datetime.timedelta(iter_target_doy - 1)
            temp_min_date = (
Exemplo n.º 3
0
def img_composite(sensor='LS',
                  bands=None,
                  pixel_resolution=30,
                  cloud_cover=70,
                  masks=None,
                  roi=None,
                  score='STM',
                  reducer=None,
                  epsg=None,
                  target_years=None,
                  surr_years=0,
                  target_doys=None,
                  doy_range=182,
                  doy_vs_year=20,
                  min_clouddistance=10,
                  max_clouddistance=50,
                  weight_doy=0.4,
                  weight_year=0.4,
                  weight_cloud=0.2,
                  buffer_clouds=False,
                  mask_percentiles=False,
                  exclude_slc_off=False,
                  export_option="Drive",
                  asset_path=None,
                  export_name=None,
                  lst_threshold=None,
                  wv_method="NCEP"):
    """
    Image compositing function capable of creating pixel-based composites (PBC) according to Griffiths et al. (2013):
    "A Pixel-Based Landsat Compositing Algorithm for Large Area Land Cover Mapping", maximum NDVI composites as well as
    spectral-temporal metrics (STMs).

    :param sensor:              (Str) sensors to use. One of S2_L1C (TOA), S2_L2A (BOA), L5, L7, L8, LS (L-5/7/8),
                                SL (LS+S2_L2A)
    :param bands:               (List) of bandnames. One of ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2', 'NDVI', 'NDWI1',
                                'NDWI2', 'TCG', 'TCB', 'TCW', 'NDBI']. Default ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'].
    :param pixel_resolution:    (Int) pixel (spatial) resolution in CRS unit (usually meters). Default to 30.
    :param cloud_cover:         (Int) maximum scene cloud cover. Default to 70.
    :param masks:               (List) of objects to mask. One of and default to ['cloud', 'cshadow', 'snow'].
    :param roi_geom:                 (List) of rectangle corner coordinates in [lon1, lat1, lon2, lat2]. Default to "Berlin".
    :param score:               (Str) Which method to use for compositing. One of "PBC", "MAXNDVI", "STM" or "NOBS"
                                (pixel wise number of observations). Default to "STM".
    :param reducer:             (ee.Reducer object) if score = "STM". Default to ee.Reducer.median().
    :param epsg:                (Str) EPSG code. Default to None will automatically detect UTM Zone.
    :param target_years:        (List) of target years for compositing. Default to [2019].
    :param surr_years:          (Int) +- years to consider around target_years. Default to 0.
    :param target_doys:         (List) of target DOYs for compositing. Default to [182].
    :param doy_range:           (Int) +- DOYs to consider around target_doys. Default to 182.
    :param doy_vs_year:         (Int) If score = "PBC". DOY at which an image with an one year offset from target_years
                                has the same score as an image in the target_year with that DOY offset.
    :param min_clouddistance:   (Int) If score = "PBC". Minimum required distance from clouds.
    :param max_clouddistance:   (Int) If score = "PBC". Distance at which the maximum cloud score is allocated.
    :param weight_doy:          (Int) If score = "PBC". Weight for the DOY score. Default to 0.4.
    :param weight_year:         (Int) If score = "PBC". Weight for the YEAR score. Default to 0.4.
    :param weight_cloud:        (Int) If score = "PBC". Weight for the CLOUD score. Default to 0.2.
    :param exclude_slc_off:     (Bool) Exclude Landsat-7 ETM+ scenes after the scan-line corrector failure
                                (i.e. after May 31, 2003). Default to False.
    :param export_option:       (Str) One of "Drive" or "Asset". Default to "Drive".
    :param asset_path:          (Str) If export_option = "Asset". Directory string to store Assets in.
    :param export_name:         (Str) Name that is appendend to the image files. E.g. if the study area is Berlin,
                                the STM = ee.Reducer.median() and the band = "NDVI" one may choose "NDVI_MEDIAN_BERLIN"
    :return:                    If successful, returns "Submitted to Server."
    """

    # defaults
    if roi is None:
        roi = [13.08, 52.32, 13.76, 52.67]  # Berlin
        print("No region of interest specified. Berlin it is.")
    if masks is None:
        masks = ['cloud', 'cshadow', 'snow']
        print("No masks specified. Masking clouds, cloud shadows and snow.")
    if sensor is None:
        sensor = "LS"
        print("No sensor specified. Landsat it is.")
    if bands is None:
        bands = ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']
        print("No bands specified. B-G-R-NIR-SWIR1-SWIR2 it is.")
    if reducer is None:
        reducer = ee.Reducer.median()
    if target_years is None:
        target_years = [2019]
        print("No target years specified. 2019 it is.")
    if target_doys is None:
        target_doys = [182]
        print("No target DOYs specified. 182 it is.")
    if (export_option == "Asset") & (asset_path is None):
        print("No asset path specified.")
        return
    if export_name is None:
        export_name = "BERLIN"

    # roi client to server
    roi_geom = ee.Geometry.Rectangle(roi)

    # find epsg
    if epsg is None:
        epsg = generals.find_utm(roi_geom)

    for year in target_years:
        for i in range(len(target_doys)):

            # time definition
            iter_target_doy = target_doys[i]
            year_min = year - surr_years
            year_max = year + surr_years
            time_filter = generals.time_filter(l_years=[year_min, year_max],
                                               l_doys=iter_target_doy,
                                               doy_offset=doy_range)
            # server side cloud distance
            REQ_DISTANCE = ee.Number(max_clouddistance)
            MIN_DISTANCE = ee.Number(min_clouddistance)

            # import collections
            imgCol_L5_SR = ee.ImageCollection('LANDSAT/LT05/C01/T1_SR') \
                .filterBounds(roi_geom) \
                .filter(time_filter) \
                .filter(ee.Filter.lt('CLOUD_COVER_LAND', cloud_cover)) \
                .map(prepro.rename_bands_l5) \
                .map(prepro.mask_landsat_sr(masks)) \
                .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'], ['TIR'])) \
                .map(prepro.scale_img(0.1, ['TIR'], ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']))

            imgCol_L7_SR = ee.ImageCollection('LANDSAT/LE07/C01/T1_SR') \
                .filterBounds(roi_geom) \
                .filter(time_filter) \
                .filter(ee.Filter.lt('CLOUD_COVER_LAND', cloud_cover)) \
                .map(prepro.rename_bands_l7) \
                .map(prepro.mask_landsat_sr(masks)) \
                .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'], ['TIR'])) \
                .map(prepro.scale_img(0.1, ['TIR'], ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']))

            # check SLC_OFF statement
            if exclude_slc_off:
                imgCol_L7_SR = imgCol_L7_SR.filter(
                    ee.Filter.date("1999-04-18", "2003-05-31"))

            imgCol_L8_SR = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR') \
                .filterBounds(roi_geom) \
                .filter(time_filter) \
                .filter(ee.Filter.lt('CLOUD_COVER_LAND', cloud_cover)) \
                .map(prepro.rename_bands_l8) \
                .map(prepro.mask_landsat_sr(masks)) \
                .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2'], ['TIR'])) \
                .map(prepro.scale_img(0.1, ['TIR'], ['B', 'G', 'R', 'NIR', 'SWIR1', 'SWIR2']))

            imgCol_S2_L1C = ee.ImageCollection('COPERNICUS/S2') \
                .filterBounds(roi_geom) \
                .filter(time_filter) \
                .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', cloud_cover)) \
                .map(prepro.mask_s2_cdi(-0.5)) \
                .map(prepro.rename_bands_s2) \
                .map(prepro.mask_s2) \
                .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'RE1', 'RE2', 'RE3', 'NIR', 'RE4', 'SWIR1', 'SWIR2']))

            imgCol_S2_L2A = ee.ImageCollection('COPERNICUS/S2_SR') \
                .filterBounds(roi_geom) \
                .filter(time_filter) \
                .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', cloud_cover)) \
                .map(prepro.mask_s2_cdi(-0.5)) \
                .map(prepro.rename_bands_s2) \
                .map(prepro.mask_s2_scl) \
                .map(prepro.scale_img(0.0001, ['B', 'G', 'R', 'RE1', 'RE2', 'RE3', 'NIR', 'RE4', 'SWIR1', 'SWIR2']))

            if 'LST' in bands:
                if imgCol_L5_SR.size().getInfo() > 0:
                    imgCol_L5_SR = lst.apply_lst_prepro(
                        imgCol_L5_SR,
                        sensor="L5",
                        time_filter=time_filter,
                        roi=roi_geom,
                        cloud_cover=cloud_cover,
                        wv_method=wv_method)
                if imgCol_L7_SR.size().getInfo() > 0:
                    imgCol_L7_SR = lst.apply_lst_prepro(
                        imgCol_L7_SR,
                        sensor="L7",
                        time_filter=time_filter,
                        roi=roi_geom,
                        cloud_cover=cloud_cover,
                        wv_method=wv_method)
                if imgCol_L8_SR.size().getInfo() > 0:
                    imgCol_L8_SR = lst.apply_lst_prepro(
                        imgCol_L8_SR,
                        sensor="L8",
                        time_filter=time_filter,
                        roi=roi_geom,
                        cloud_cover=cloud_cover,
                        wv_method=wv_method)

            if 'ALBEDO' in bands:
                imgCol_L5_SR = imgCol_L5_SR.map(
                    prepro.surface_albedo(sensor="L5"))
                imgCol_L7_SR = imgCol_L7_SR.map(
                    prepro.surface_albedo(sensor="L7"))
                imgCol_L8_SR = imgCol_L8_SR.map(
                    prepro.surface_albedo(sensor="L8"))

            # --------------------------------------------------
            # MERGE imgCols
            # --------------------------------------------------
            if sensor == 'S2_L1C':
                imgCol_SR = imgCol_S2_L1C
            elif sensor == 'S2_L2A':
                imgCol_SR = imgCol_S2_L2A
            elif sensor == 'LS':
                imgCol_SR = imgCol_L5_SR.merge(imgCol_L7_SR).merge(
                    imgCol_L8_SR)
                imgCol_SR = imgCol_SR.sort("system:time_start")
            elif sensor == 'L8':
                imgCol_SR = imgCol_L8_SR
            elif sensor == 'L7':
                imgCol_SR = imgCol_L7_SR
            elif sensor == 'L5':
                imgCol_SR = imgCol_L5_SR
            elif sensor == 'SL8':
                imgCol_SR = imgCol_L8_SR.merge(imgCol_S2_L2A)
            elif sensor == 'SL':
                imgCol_SR = imgCol_L5_SR.merge(imgCol_L7_SR).merge(
                    imgCol_L8_SR).merge(imgCol_S2_L2A)
            else:
                imgCol_SR = None
                print('No sensor specified!')

            # --------------------------------------------------
            # Calculate Indices
            # --------------------------------------------------
            if ('NDVI' in bands) or (score == 'MAXNDVI') or ('LST' in bands):
                imgCol_SR = imgCol_SR.map(prepro.ndvi)
            if 'EVI' in bands:
                imgCol_SR = imgCol_SR.map(prepro.evi())
            if 'NDWI1' in bands:
                imgCol_SR = imgCol_SR.map(prepro.ndwi1)
            if ('NDWI2' in bands) or ('LST' in bands):
                imgCol_SR = imgCol_SR.map(prepro.ndwi2)
            if 'NDBI' in bands:
                imgCol_SR = imgCol_SR.map(prepro.ndbi)
            if 'TCG' in bands:
                imgCol_SR = imgCol_SR.map(prepro.tcg)
            if 'TCB' in bands:
                imgCol_SR = imgCol_SR.map(prepro.tcb)
            if 'TCW' in bands:
                imgCol_SR = imgCol_SR.map(prepro.tcw)

            if 'LST' in bands:
                imgCol_SR = imgCol_SR.map(
                    prepro.fvc(ndvi_soil=0.15, ndvi_vegetation=0.9))
                imgCol_SR = imgCol_SR.map(lst.emissivity())
                imgCol_SR = imgCol_SR.map(
                    lst.land_surface_temperature(scale=0.01))
                if lst_threshold:
                    imgCol_SR = imgCol_SR.map(
                        lst.mask_lst(threshold=lst_threshold, scale=0.01))

            # --------------------------------------------------
            # Add DOY, YEAR & CLOUD Bands to ImgCol
            # --------------------------------------------------
            imgCol_SR = imgCol_SR.map(composite.fun_add_doy_band)
            imgCol_SR = imgCol_SR.map(composite.fun_addyearband)
            imgCol_SR = imgCol_SR.map(
                composite.fun_addcloudband(req_distance=max_clouddistance))

            if buffer_clouds:
                imgCol_SR = imgCol_SR.map(
                    prepro.mask_cloudbuffer(min_distance=min_clouddistance))

            # apply percentile masking (optional)
            if mask_percentiles:
                blwr = imgCol_SR.select('R').reduce(ee.Reducer.percentile([5]))
                bupr = imgCol_SR.select('B').reduce(ee.Reducer.percentile([95
                                                                           ]))
                imgCol_SR = imgCol_SR.map(
                    prepro.mask_percentiles(band_lwr='R',
                                            band_upr='B',
                                            lwr=blwr,
                                            upr=bupr))

            if score == 'PBC':
                # --------------------------------------------------
                # SCORING 1: DOY
                # --------------------------------------------------
                # add DOY-band to images in imgCol
                doys = imgCol_SR.map(
                    composite.fun_doys).aggregate_array('doy').getInfo()

                # retrieve target-DOY and DOY-std (client and server side)
                target_doy = ee.Number(iter_target_doy)

                doy_std_client = np.std(doys)

                doy_std = ee.Number(doy_std_client)

                # add Band with final DOY score to every image in imgCol
                imgCol_SR = imgCol_SR.map(
                    composite.doyscore(doy_std, target_doy))

                # --------------------------------------------------
                # SCORING 2: YEAR
                # --------------------------------------------------
                # calculate DOY-score at maximum DOY vs Year threshold
                doyscore_offset = composite.doyscore_offset(
                    iter_target_doy - doy_vs_year, iter_target_doy,
                    doy_std_client)
                doyscore_offset_obj = ee.Number(doyscore_offset)
                target_years_obj = ee.Number(year)

                # add Band with final YEAR score to every image in imgCol
                imgCol_SR = imgCol_SR.map(
                    composite.yearscore(target_years_obj, doyscore_offset_obj))

                # --------------------------------------------------
                # SCORING 3: CLOUD DISTANCE
                # --------------------------------------------------
                imgCol_SR = imgCol_SR.map(
                    composite.cloudscore(REQ_DISTANCE, MIN_DISTANCE))

                # --------------------------------------------------
                # FINAL SCORING
                # --------------------------------------------------
                w_doyscore = ee.Number(weight_doy)
                w_yearscore = ee.Number(weight_year)
                w_cloudscore = ee.Number(weight_cloud)

                imgCol_SR = imgCol_SR.map(
                    composite.score(w_doyscore, w_yearscore, w_cloudscore))

                img_composite = imgCol_SR.qualityMosaic(score)

                # check if DOY and/or YEAR are selected
                l_meta_bands = ['YEAR', 'DOY']
                if any(x in bands for x in l_meta_bands):
                    normal_scale_bands = [
                        x for x in bands if x not in l_meta_bands
                    ]
                    meta_scale_bands = [x for x in bands if x in l_meta_bands]
                    normal_img_composite = img_composite.select(
                        normal_scale_bands)
                    meta_img_composite = img_composite.select(meta_scale_bands)
                    normal_img_composite = normal_img_composite.multiply(10000)
                    img_composite = normal_img_composite.addBands(
                        meta_img_composite)
                else:
                    img_composite = img_composite.select(bands)
                    img_composite = img_composite.multiply(10000)
                img_composite = img_composite.int16()

            elif score == 'MAXNDVI':
                img_composite = imgCol_SR.qualityMosaic('NDVI')
                img_composite = img_composite.select(bands)
                img_composite = img_composite.multiply(10000)
                img_composite = img_composite.int16()

            elif score == 'STM':
                img_composite = ee.Image(
                    imgCol_SR.select(bands).reduce(reducer))
                img_composite = img_composite.multiply(10000)
                img_composite = img_composite.int16()

            elif score == 'TS_slope':
                imgCol_SR = imgCol_SR.map(generals.add_timeband())
                for i in range(len(bands)):
                    slope = imgCol_SR.select(['TIME', bands[i]
                                              ]).reduce(ee.Reducer.sensSlope())
                    slope = slope.select('slope')
                    slope = slope.multiply(365.25)  # yearly increase
                    slope = slope.multiply(10000).rename(bands[i] + '_slope')
                    slope = slope.int16()
                    if i == 0:
                        img_composite = ee.Image(slope)
                    else:
                        img_composite = img_composite.addBands(slope)

            elif score == 'NOBS':
                img_composite = imgCol_SR.select(
                    bands[0]).count().rename('NOBS')
                img_composite = img_composite.int16()

            else:
                print(
                    "Invalid score specified. Must be one of 'PBC', 'MAXNDVI', 'STM' or 'NOBS'"
                )

            # output filename
            out_file = sensor + '_' + score + '_' + export_name + '_' + \
                       str(pixel_resolution) + 'm_' + str(iter_target_doy) + '-' + str(doy_range) + \
                        '_' + str(year) + '-' + str(surr_years)

            # export image
            if export_option == "Drive":
                out = ee.batch.Export.image.toDrive(
                    image=img_composite,
                    description=out_file,
                    scale=pixel_resolution,
                    maxPixels=1e13,
                    region=roi_geom['coordinates'][0],
                    crs=epsg)
            elif export_option == "Asset":
                out = ee.batch.Export.image.toAsset(
                    image=img_composite,
                    description=out_file,
                    assetId=asset_path + out_file,
                    scale=pixel_resolution,
                    maxPixels=1e13,
                    region=roi_geom['coordinates'][0],
                    crs=epsg)
            else:
                print(
                    "Invalid export option specified. Must be one of 'Drive' or 'Asset'"
                )

            process = ee.batch.Task.start(out)

    return print("Submitted to Server.")