Ejemplo n.º 1
0
def tirs_to_at_satellite_temperature(
        tirs_1x,
        mtl_file,
        brightness_temperature_prefix=None,
        null=False,
        quiet=True):
    """
    Helper function to convert TIRS bands 10 or 11 in to at-satellite
    temperatures.

    This function uses the pre-defined functions:

    - extract_number_from_string()
    - digital_numbers_to_radiance()
    - radiance_to_brightness_temperature()

    The inputs are:

    - a name for the input tirs band (10 or 11)
    - a Landsat8 MTL file

    The output is a temporary at-Satellite Temperature map.
    """
    # which band number and MTL file
    band_number = extract_number_from_string(tirs_1x)
    tmp_radiance = tmp_map_name('radiance') + '.' + band_number
    tmp_brightness_temperature = tmp_map_name('brightness_temperature') + '.' + \
        band_number
    landsat8 = Landsat8_MTL(mtl_file)

    # rescale DNs to spectral radiance
    radiance_expression = landsat8.toar_radiance(band_number)
    digital_numbers_to_radiance(
        tmp_radiance,
        tirs_1x,
        radiance_expression,
        null,
        quiet,
    )

    # convert spectral radiance to at-satellite temperature
    temperature_expression = landsat8.radiance_to_temperature(band_number)
    radiance_to_brightness_temperature(
        tmp_brightness_temperature,
        tmp_radiance,
        temperature_expression,
        quiet,
    )

    # save Brightness Temperature map?
    if brightness_temperature_prefix:
        bt_output = brightness_temperature_prefix + band_number
        run('g.rename', raster=(tmp_brightness_temperature, bt_output))
        tmp_brightness_temperature = bt_output

    return tmp_brightness_temperature
Ejemplo n.º 2
0
def add_timestamp(mtl_filename, outname):
    """
    Retrieve metadata from MTL file.
    """
    import datetime
    metadata = Landsat8_MTL(mtl_filename)

    # required format is: day=integer month=string year=integer time=hh:mm:ss.dd
    acquisition_date = str(metadata.date_acquired)  ### FixMe ###
    acquisition_date = datetime.datetime.strptime(
        acquisition_date, '%Y-%m-%d').strftime('%d %b %Y')
    acquisition_time = str(metadata.scene_center_time)[0:8]
    date_time_string = acquisition_date + ' ' + acquisition_time

    #msg = "Date and time of acquisition: " + date_time_string
    #grass.verbose(msg)

    run('r.timestamp', map=outname, date=date_time_string)
Ejemplo n.º 3
0
def test(mtlfile):
    """
    Test the class.
    """

    print("! No file defined, testing with default MTl file!")
    mtl = Landsat8_MTL(MTLFILE)
    print()
    print("| Test the object's __str__ method:", mtl)
    print("| Test method _get_mtl_lines:\n ", mtl._get_mtl_lines())
    print()
    print("| Basic metadata:")
    print("  > id (original field name is 'LANDSAT_SCENE_ID'):", mtl.scene_id)
    print("  > WRS path:", mtl.wrs_path)
    print("  > WRS row:", mtl.wrs_row)
    print("  > Acquisition date:", mtl.date_acquired)
    print("  > Scene's center time:", mtl.scene_center_time)
    print("  > Upper left corner:", mtl.corner_ul)
    print("  > Lower right corner:", mtl.corner_lr)
    print("  > Upper left (projected):", mtl.corner_ul_projection)
    print("  > Lower right (projected):", mtl.corner_lr_projection)
    print("  > Cloud cover:", mtl.cloud_cover)
def main():
    # Temporary filenames
    tmp_avg_lse = tmp_map_name('avg_lse')
    tmp_delta_lse = tmp_map_name('delta_lse')
    tmp_cwv = tmp_map_name('cwv')
    #tmp_lst = tmp_map_name('lst')

    # user input
    mtl_file = options['mtl']

    if not options['prefix']:
        b10 = options['b10']
        b11 = options['b11']
        t10 = options['t10']
        t11 = options['t11']

        if not options['clouds']:
            qab = options['qab']
            cloud_map = False

        else:
            qab = False
            cloud_map = options['clouds']

    elif options['prefix']:
        prefix = options['prefix']
        b10 = prefix + '10'
        b11 = prefix + '11'

        if not options['clouds']:
            qab = prefix + 'QA'
            cloud_map = False

        else:
            cloud_map = options['clouds']
            qab = False

    qapixel = options['qapixel']
    lst_output = options['lst']

    # save Brightness Temperature maps?
    if options['prefix_bt']:
        brightness_temperature_prefix = options['prefix_bt']
    else:
        brightness_temperature_prefix = None

    cwv_window_size = int(options['window'])
    assertion_for_cwv_window_size_msg = (
        'A spatial window of size 5^2 or less is not '
        'recommended. Please select a larger window. '
        'Refer to the manual\'s notes for details.')
    assert cwv_window_size >= 7, assertion_for_cwv_window_size_msg
    cwv_output = options['cwv']

    # optional maps
    average_emissivity_map = options['emissivity']
    delta_emissivity_map = options['delta_emissivity']

    # output for in-between maps?
    emissivity_output = options['emissivity_out']
    delta_emissivity_output = options['delta_emissivity_out']
    landcover_map = options['landcover']
    landcover_class = options['landcover_class']

    # flags
    info = flags['i']
    scene_extent = flags['e']
    timestamping = flags['t']
    null = flags['n']
    rounding = flags['r']
    celsius = flags['c']

    #
    # Pre-production actions
    #

    # Set Region
    if scene_extent:
        grass.use_temp_region()  # safely modify the region
        msg = "\n|! Matching region extent to map {name}"

        # ToDo: check if extent-B10 == extent-B11? Unnecessary?
        # Improve below!

        if b10:
            run('g.region', rast=b10, align=b10)
            msg = msg.format(name=b10)

        elif t10:
            run('g.region', rast=t10, align=t10)
            msg = msg.format(name=t10)

        g.message(msg)

    elif not scene_extent:
        grass.warning(_('Operating on current region'))

    #
    # 1. Mask clouds
    #

    if cloud_map:
        # user-fed cloud map?
        msg = '\n|i Using {cmap} as a MASK'.format(cmap=cloud_map)
        g.message(msg)
        r.mask(raster=cloud_map, flags='i', overwrite=True)

    else:
        # using the quality assessment band and a "QA" pixel value
        mask_clouds(qab, qapixel)

    #
    # 2. TIRS > Brightness Temperatures
    #

    if mtl_file:
        # if MTL and b10 given, use it to compute at-satellite temperature t10
        if b10:
            t10 = tirs_to_at_satellite_temperature(
                b10,
                mtl_file,
                brightness_temperature_prefix,
                null,
                quiet=info,
            )

        # likewise for b11 -> t11
        if b11:
            t11 = tirs_to_at_satellite_temperature(
                b11,
                mtl_file,
                brightness_temperature_prefix,
                null,
                quiet=info,
            )

    #
    # Initialise a SplitWindowLST object
    #

    split_window_lst = SplitWindowLST(landcover_class)
    citation_lst = split_window_lst.citation

    #
    # 3. Land Surface Emissivities
    #

    # use given fixed class?
    if landcover_class:

        if split_window_lst.landcover_class is False:
            # replace with meaningful error
            grass.warning('Unknown land cover class string! Note, this string '
                          'input option is case sensitive.')

        if landcover_class == 'Random':
            msg = "\n|! Random emissivity class selected > " + \
                split_window_lst.landcover_class + ' '

        if landcover_class == 'Barren_Land':
            msg = "\n|! For barren land, the last quadratic term of the Split-Window algorithm will be set to 0" + \
                split_window_lst.landcover_class + ' '

        else:
            msg = '\n|! Retrieving average emissivities *only* for {eclass} '

        if info:
            msg += '| Average emissivities (channels 10, 11): '
            msg += str(split_window_lst.emissivity_t10) + ', ' + \
                str(split_window_lst.emissivity_t11)

        msg = msg.format(eclass=split_window_lst.landcover_class)
        g.message(msg)

    # use the FROM-GLC map
    elif landcover_map:

        if average_emissivity_map:
            tmp_avg_lse = average_emissivity_map

        if not average_emissivity_map:
            determine_average_emissivity(
                tmp_avg_lse,
                emissivity_output,
                landcover_map,
                split_window_lst.average_lse_mapcalc,
                quiet=info,
            )
            if options['emissivity_out']:
                tmp_avg_lse = options['emissivity_out']

        if delta_emissivity_map:
            tmp_delta_lse = delta_emissivity_map

        if not delta_emissivity_map:
            determine_delta_emissivity(
                tmp_delta_lse,
                delta_emissivity_output,
                landcover_map,
                split_window_lst.delta_lse_mapcalc,
                quiet=info,
            )
            if options['delta_emissivity_out']:
                tmp_delta_lse = options['delta_emissivity_out']

    #
    # 4. Modified Split-Window Variance-Covariance Matrix > Column Water Vapor
    #

    if info:
        msg = '\n|i Spatial window of size {n} for Column Water Vapor estimation: '
        msg = msg.format(n=cwv_window_size)
        g.message(msg)

    cwv = Column_Water_Vapor(cwv_window_size, t10, t11)
    citation_cwv = cwv.citation
    estimate_cwv_big_expression(
        tmp_cwv,
        cwv_output,
        t10,
        t11,
        cwv._big_cwv_expression(),
    )
    if cwv_output:
        tmp_cwv = cwv_output

    #
    # 5. Estimate Land Surface Temperature
    #

    if info and landcover_class == 'Random':
        msg = '\n|* Will pick a random emissivity class!'
        grass.verbose(msg)

    estimate_lst(
        lst_output,
        t10,
        t11,
        landcover_map,
        landcover_class,
        tmp_avg_lse,
        tmp_delta_lse,
        tmp_cwv,
        split_window_lst.sw_lst_mapcalc,
        rounding,
        celsius,
        quiet=info,
    )

    #
    # Post-production actions
    #

    # remove MASK
    r.mask(flags='r', verbose=True)

    # time-stamping
    if timestamping:
        add_timestamp(mtl_file, lst_output)

        if cwv_output:
            add_timestamp(mtl_file, cwv_output)

    # Apply color table
    if celsius:
        run('r.colors', map=lst_output, color='celsius')
    else:
        # color table for kelvin
        run('r.colors', map=lst_output, color='kelvin')

    # ToDo: helper function for r.support
    # strings for metadata
    history_lst = '\n' + citation_lst
    history_lst += '\n\n' + citation_cwv
    history_lst += '\n\nSplit-Window model: '
    history_lst += split_window_lst._equation  # :wsw_lst_mapcalc
    description_lst = (
        'Land Surface Temperature derived from a split-window algorithm. ')

    if celsius:
        title_lst = 'Land Surface Temperature (C)'
        units_lst = 'Celsius'

    else:
        title_lst = 'Land Surface Temperature (K)'
        units_lst = 'Kelvin'

    landsat8_metadata = Landsat8_MTL(mtl_file)
    source1_lst = landsat8_metadata.scene_id
    source2_lst = landsat8_metadata.origin

    # history entry
    run(
        "r.support",
        map=lst_output,
        title=title_lst,
        units=units_lst,
        description=description_lst,
        source1=source1_lst,
        source2=source2_lst,
        history=history_lst,
    )

    # restore region
    if scene_extent:
        grass.del_temp_region()  # restoring previous region settings
        g.message("|! Original Region restored")

    # print citation
    if info:
        g.message('\nSource: ' + citation_lst)
Ejemplo n.º 5
0
def main():
    # Temporary filenames
    tmp_avg_lse = tmp_map_name("avg_lse")
    tmp_delta_lse = tmp_map_name("delta_lse")
    tmp_cwv = tmp_map_name("cwv")
    # tmp_lst = tmp_map_name('lst')

    # user input
    mtl_file = options["mtl"]

    if not options["prefix"]:
        b10 = options["b10"]
        b11 = options["b11"]
        t10 = options["t10"]
        t11 = options["t11"]

        if not options["clouds"]:
            qab = options["qab"]
            cloud_map = False

        else:
            qab = False
            cloud_map = options["clouds"]

    elif options["prefix"]:
        prefix = options["prefix"]
        b10 = prefix + "10"
        b11 = prefix + "11"

        if not options["clouds"]:
            qab = prefix + "QA"
            cloud_map = False

        else:
            cloud_map = options["clouds"]
            qab = False

    qapixel = options["qapixel"]
    lst_output = options["lst"]

    # save Brightness Temperature maps?
    if options["prefix_bt"]:
        brightness_temperature_prefix = options["prefix_bt"]
    else:
        brightness_temperature_prefix = None

    cwv_window_size = int(options["window"])
    assertion_for_cwv_window_size_msg = (
        "A spatial window of size 5^2 or less is not "
        "recommended. Please select a larger window. "
        "Refer to the manual's notes for details.")
    assert cwv_window_size >= 7, assertion_for_cwv_window_size_msg
    cwv_output = options["cwv"]

    # optional maps
    average_emissivity_map = options["emissivity"]
    delta_emissivity_map = options["delta_emissivity"]

    # output for in-between maps?
    emissivity_output = options["emissivity_out"]
    delta_emissivity_output = options["delta_emissivity_out"]
    landcover_map = options["landcover"]
    landcover_class = options["landcover_class"]

    # flags
    info = flags["i"]
    scene_extent = flags["e"]
    timestamping = flags["t"]
    null = flags["n"]
    rounding = flags["r"]
    celsius = flags["c"]

    # ToDo:
    # shell = flags['g']

    #
    # Pre-production actions
    #

    # Set Region
    if scene_extent:
        grass.use_temp_region()  # safely modify the region
        msg = "\n|! Matching region extent to map {name}"

        # ToDo: check if extent-B10 == extent-B11? Unnecessary?
        # Improve below!

        if b10:
            run("g.region", rast=b10, align=b10)
            msg = msg.format(name=b10)

        elif t10:
            run("g.region", rast=t10, align=t10)
            msg = msg.format(name=t10)

        g.message(msg)

    elif not scene_extent:
        grass.warning(_("Operating on current region"))

    #
    # 1. Mask clouds
    #

    if cloud_map:
        # user-fed cloud map?
        msg = "\n|i Using {cmap} as a MASK".format(cmap=cloud_map)
        g.message(msg)
        r.mask(raster=cloud_map, flags="i", overwrite=True)

    else:
        # using the quality assessment band and a "QA" pixel value
        mask_clouds(qab, qapixel)

    #
    # 2. TIRS > Brightness Temperatures
    #

    if mtl_file:

        # if MTL and b10 given, use it to compute at-satellite temperature t10
        if b10:
            # convert DNs to at-satellite temperatures
            t10 = tirs_to_at_satellite_temperature(
                b10,
                mtl_file,
                brightness_temperature_prefix,
                null,
                quiet=info,
            )

        # likewise for b11 -> t11
        if b11:
            # convert DNs to at-satellite temperatures
            t11 = tirs_to_at_satellite_temperature(
                b11,
                mtl_file,
                brightness_temperature_prefix,
                null,
                quiet=info,
            )

    #
    # Initialise a SplitWindowLST object
    #

    split_window_lst = SplitWindowLST(landcover_class)
    citation_lst = split_window_lst.citation

    #
    # 3. Land Surface Emissivities
    #

    # use given fixed class?
    if landcover_class:

        if split_window_lst.landcover_class is False:
            # replace with meaningful error
            grass.warning("Unknown land cover class string! Note, this string "
                          "input option is case sensitive.")

        if landcover_class == "Random":
            msg = ("\n|! Random emissivity class selected > " +
                   split_window_lst.landcover_class + " ")

        if landcover_class == "Barren_Land":
            msg = (
                "\n|! For barren land, the last quadratic term of the Split-Window algorithm will be set to 0"
                + split_window_lst.landcover_class + " ")

        else:
            msg = "\n|! Retrieving average emissivities *only* for {eclass} "

        if info:
            msg += "| Average emissivities (channels 10, 11): "
            msg += (str(split_window_lst.emissivity_t10) + ", " +
                    str(split_window_lst.emissivity_t11))

        msg = msg.format(eclass=split_window_lst.landcover_class)
        g.message(msg)

    # use the FROM-GLC map
    elif landcover_map:

        if average_emissivity_map:
            tmp_avg_lse = average_emissivity_map

        if not average_emissivity_map:
            determine_average_emissivity(
                tmp_avg_lse,
                emissivity_output,
                landcover_map,
                split_window_lst.average_lse_mapcalc,
                quiet=info,
            )
            if options["emissivity_out"]:
                tmp_avg_lse = options["emissivity_out"]

        if delta_emissivity_map:
            tmp_delta_lse = delta_emissivity_map

        if not delta_emissivity_map:
            determine_delta_emissivity(
                tmp_delta_lse,
                delta_emissivity_output,
                landcover_map,
                split_window_lst.delta_lse_mapcalc,
                quiet=info,
            )
            if options["delta_emissivity_out"]:
                tmp_delta_lse = options["delta_emissivity_out"]

    #
    # 4. Modified Split-Window Variance-Covariance Matrix > Column Water Vapor
    #

    if info:
        msg = "\n|i Spatial window of size {n} for Column Water Vapor estimation: "
        msg = msg.format(n=cwv_window_size)
        g.message(msg)

    cwv = Column_Water_Vapor(cwv_window_size, t10, t11)
    citation_cwv = cwv.citation
    estimate_cwv_big_expression(
        tmp_cwv,
        cwv_output,
        t10,
        t11,
        cwv._big_cwv_expression(),
    )
    if cwv_output:
        tmp_cwv = cwv_output

    #
    # 5. Estimate Land Surface Temperature
    #

    if info and landcover_class == "Random":
        msg = "\n|* Will pick a random emissivity class!"
        grass.verbose(msg)

    estimate_lst(
        lst_output,
        t10,
        t11,
        landcover_map,
        landcover_class,
        tmp_avg_lse,
        tmp_delta_lse,
        tmp_cwv,
        split_window_lst.sw_lst_mapcalc,
        rounding,
        celsius,
        quiet=info,
    )

    #
    # Post-production actions
    #

    # remove MASK
    r.mask(flags="r", verbose=True)

    # time-stamping
    if timestamping:
        add_timestamp(mtl_file, lst_output)

        if cwv_output:
            add_timestamp(mtl_file, cwv_output)

    # Apply color table
    if celsius:
        run("r.colors", map=lst_output, color="celsius")
    else:
        # color table for kelvin
        run("r.colors", map=lst_output, color="kelvin")

    # ToDo: helper function for r.support
    # strings for metadata
    history_lst = "\n" + citation_lst
    history_lst += "\n\n" + citation_cwv
    history_lst += "\n\nSplit-Window model: "
    history_lst += split_window_lst._equation  # :wsw_lst_mapcalc
    description_lst = "Land Surface Temperature derived from a split-window algorithm. "

    if celsius:
        title_lst = "Land Surface Temperature (C)"
        units_lst = "Celsius"

    else:
        title_lst = "Land Surface Temperature (K)"
        units_lst = "Kelvin"

    landsat8_metadata = Landsat8_MTL(mtl_file)
    source1_lst = landsat8_metadata.scene_id
    source2_lst = landsat8_metadata.origin

    # history entry
    run(
        "r.support",
        map=lst_output,
        title=title_lst,
        units=units_lst,
        description=description_lst,
        source1=source1_lst,
        source2=source2_lst,
        history=history_lst,
    )

    # (re)name the LST product
    # run("g.rename", rast=(tmp_lst, lst_output))

    # restore region
    if scene_extent:
        grass.del_temp_region()  # restoring previous region settings
        g.message("|! Original Region restored")

    # print citation
    if info:
        g.message("\nSource: " + citation_lst)
Ejemplo n.º 6
0
def main():
    # Temporary filenames
    tmp_avg_lse = tmp_map_name('avg_lse')
    tmp_delta_lse = tmp_map_name('delta_lse')
    #tmp_lst = tmp_map_name('lst')

    # user input
    mtl_file = options['mtl']

    if not options['prefix']:
        b10 = options['b10']
        b11 = options['b11']
        t10 = options['t10']
        t11 = options['t11']

        if not options['clouds']:
            qab = options['qab']
            cloud_map = False

        else:
            qab = False
            cloud_map = options['clouds']

    elif options['prefix']:
        prefix = options['prefix']
        b10 = prefix + '10'
        b11 = prefix + '11'

        if not options['clouds']:
            qab = prefix + 'QA'
            cloud_map = False

        else:
            cloud_map = options['clouds']
            qab = False

    qapixel = options['qapixel']
    lst_output = options['lst']

    # save Brightness Temperature maps?
    if options['prefix_bt']:
        brightness_temperature_prefix = options['prefix_bt']
    else:
        brightness_temperature_prefix = None

    if options['cwv']:
        tmp_cwv = options['cwv']
    else:
        tmp_cwv = tmp_map_name('cwv')
        cwv_window_size = int(options['window'])
        assertion_for_cwv_window_size_msg = MSG_ASSERTION_WINDOW_SIZE
        assert cwv_window_size >= 7, assertion_for_cwv_window_size_msg
    cwv_output = options['cwv_out']

    # optional maps
    average_emissivity_map = options['emissivity']
    delta_emissivity_map = options['delta_emissivity']

    # output for in-between maps?
    emissivity_output = options['emissivity_out']
    delta_emissivity_output = options['delta_emissivity_out']

    landcover_map = options['landcover']
    landcover_class = options['landcover_class']

    # flags
    info = flags['i']
    null = flags['n']
    scene_extent = flags['e']
    median = flags['m']
    accuracy = flags['a']
    rounding = flags['r']
    celsius = flags['c']
    timestamping = flags['t']

    #
    # Pre-production actions
    #

    if scene_extent:
        grass.use_temp_region()  # safely modify the region, restore at end
        msg = WARNING_REGION_MATCHING

        # TODO: Check if extent-B10 == extent-B11? #
        if b10:
            run('g.region', rast=b10, align=b10)
            msg = msg.format(name=b10)

        elif t10:
            run('g.region', rast=t10, align=t10)
            msg = msg.format(name=t10)
        # ---------------------------------------- #

        grass.warning(_(msg))

    #
    # 1. Mask clouds
    #

    if cloud_map:
        msg = f'\n|i Using user defined \'{cloud_map}\' as a MASK'
        g.message(msg)
        r.mask(raster=cloud_map, flags='i', overwrite=True)

    else:
        # using the quality assessment band and a "QA" pixel value
        mask_clouds(qab, qapixel)

    #
    # 2. TIRS > Brightness Temperatures
    #

    if mtl_file:
        # if MTL and b10 given, use it to compute at-satellite temperature t10
        if b10:
            t10 = tirs_to_at_satellite_temperature(
                b10,
                mtl_file,
                brightness_temperature_prefix,
                null,
                info=info,
            )
        # likewise for b11 -> t11
        if b11:
            t11 = tirs_to_at_satellite_temperature(
                b11,
                mtl_file,
                brightness_temperature_prefix,
                null,
                info=info,
            )

    #
    # 3. Land Surface Emissivities
    #

    split_window_lst = SplitWindowLST(landcover_class)

    if landcover_class:

        if split_window_lst.landcover_class is False:
            # replace with meaningful error
            grass.warning(MSG_UNKNOWN_LANDCOVER_CLASS)

        if landcover_class == 'Random':
            msg = MSG_RANDOM_EMISSIVITY_CLASS + \
                split_window_lst.landcover_class + ' '

        if landcover_class == 'Barren_Land':
            msg = MSG_BARREN_LAND + \
                split_window_lst.landcover_class + ' '

        else:
            msg = MSG_SINGLE_CLASS_AVERAGE_EMISSIVITY + f'{eclass} '

        if info:
            msg += MSG_AVERAGE_EMISSIVITIES
            msg += str(split_window_lst.emissivity_t10) + ', ' + \
                str(split_window_lst.emissivity_t11)

        g.message(msg)

    # use the FROM-GLC map
    elif landcover_map:

        if average_emissivity_map:
            tmp_avg_lse = average_emissivity_map

        if not average_emissivity_map:
            determine_average_emissivity(
                tmp_avg_lse,
                emissivity_output,
                landcover_map,
                split_window_lst.average_lse_mapcalc,
                info=info,
            )
            if options['emissivity_out']:
                tmp_avg_lse = options['emissivity_out']

        if delta_emissivity_map:
            tmp_delta_lse = delta_emissivity_map

        if not delta_emissivity_map:
            determine_delta_emissivity(
                tmp_delta_lse,
                delta_emissivity_output,
                landcover_map,
                split_window_lst.delta_lse_mapcalc,
                info=info,
            )
            if options['delta_emissivity_out']:
                tmp_delta_lse = options['delta_emissivity_out']

    #
    # 4. Estimate Column Water Vapor
    #

    if not options['cwv']:
        estimate_cwv(
            temporary_map=tmp_cwv,
            cwv_map=cwv_output,
            t10=t10,
            t11=t11,
            window_size=cwv_window_size,
            median=median,
            info=info,
        )
    else:
        msg = f'\n|! User defined map \'{tmp_cwv}\' for atmospheric column water vapor'
        g.message(msg)

    if cwv_output:
        tmp_cwv = cwv_output

    #
    # 5. Estimate Land Surface Temperature
    #

    if info and landcover_class == 'Random':
        msg = MSG_PICK_RANDOM_CLASS
        grass.verbose(msg)

    estimate_lst(
        outname=lst_output,
        t10=t10,
        t11=t11,
        landcover_map=landcover_map,
        landcover_class=landcover_class,
        avg_lse_map=tmp_avg_lse,
        delta_lse_map=tmp_delta_lse,
        cwv_map=tmp_cwv,
        lst_expression=split_window_lst.sw_lst_mapcalc,
        rounding=rounding,
        celsius=celsius,
        info=info,
    )

    #
    # Post-production actions
    #

    # remove MASK
    r.mask(flags='r', verbose=True)

    if timestamping:
        add_timestamp(mtl_file, lst_output)

        if cwv_output:
            add_timestamp(mtl_file, cwv_output)

    if celsius:
        run('r.colors', map=lst_output, color='celsius')

    else:
        run('r.colors', map=lst_output, color='kelvin')

    # metadata

    history_lst = '\n' + CITATION_SPLIT_WINDOW
    history_lst += '\n\n' + CITATION_COLUMN_WATER_VAPOR
    history_lst += '\n\nSplit-Window model: '
    history_lst += split_window_lst._equation  # :wsw_lst_mapcalc
    description_lst = DESCRIPTION_LST
    if celsius:
        title_lst = 'Land Surface Temperature (C)'
        units_lst = 'Celsius'
    else:
        title_lst = 'Land Surface Temperature (K)'
        units_lst = 'Kelvin'
    landsat8_metadata = Landsat8_MTL(mtl_file)
    source1_lst = landsat8_metadata.scene_id
    source2_lst = landsat8_metadata.origin
    run(
        "r.support",
        map=lst_output,
        title=title_lst,
        units=units_lst,
        description=description_lst,
        source1=source1_lst,
        source2=source2_lst,
        history=history_lst,
    )

    if scene_extent:
        grass.del_temp_region()  # restoring previous region
        grass.warning(WARNING_REGION_RESTORING)

    if info:
        g.message('\nSource: ' + CITATION_SPLIT_WINDOW)