From 1dc351c2bf1a3c1107b59158cf79ddcfe4d68956 Mon Sep 17 00:00:00 2001 From: Gabriel Soares <70075435+soaressgabriel@users.noreply.github.com> Date: Fri, 23 Feb 2024 20:47:34 -0300 Subject: [PATCH 1/2] Change recording of the raster series resulting from the simulation --- rubem/_dynamic_model.py | 27 ++-- rubem/configuration/model_configuration.py | 2 + rubem/configuration/output_raster_base.py | 15 ++ rubem/file/_file_generators.py | 160 ++++++++++----------- 4 files changed, 97 insertions(+), 107 deletions(-) create mode 100644 rubem/configuration/output_raster_base.py diff --git a/rubem/_dynamic_model.py b/rubem/_dynamic_model.py index 074ef2a..ea90d8b 100644 --- a/rubem/_dynamic_model.py +++ b/rubem/_dynamic_model.py @@ -34,16 +34,6 @@ def __init__(self, config: ModelConfiguration): self.logger.info("Obtaining grid cell area...") self.A = self.config.grid.area - self.logger.debug("Reading DEM reference file...") - try: - self.ref = getRefInfo(self.config.raster_files.demtif) - except RuntimeError: - self.logger.error( - "Error reading Digital Elevation Model (DEM) file at '%s'", - self.config.raster_files.demtif, - ) - raise - self.__getEnabledOutputVars() def __getEnabledOutputVars(self): @@ -80,19 +70,18 @@ def __stepReport(self): # Export TIFF raster series if self.config.output_variables.file_format is OutputFileFormat.GEOTIFF: - reportTIFFSeries( - self, - self.ref, - self.outputVarsDict.get(outputVar), - outputVar, - self.config.output_directory.path, - self.currentStep, - dyn=True, + report( + variable=self.outputVarsDict.get(outputVar), + name=outputVar, + timestep=self.currentStep, + outpath=self.config.output_directory.path, + format=OutputFileFormat.GEOTIFF, + base_raster_info=self.config.output_raster_base, ) # Export PCRaster map format raster series if self.config.output_variables.file_format is OutputFileFormat.PCRASTER: - self.report(self.outputVarsDict.get(outputVar), outputVar) + self.report(variable=self.outputVarsDict.get(outputVar), name=outputVar) # Check if we have to export the time series of the selected # variable (fileName) diff --git a/rubem/configuration/model_configuration.py b/rubem/configuration/model_configuration.py index 6528494..556e1c9 100644 --- a/rubem/configuration/model_configuration.py +++ b/rubem/configuration/model_configuration.py @@ -14,6 +14,7 @@ from rubem.configuration.model_constants import ModelConstants from rubem.configuration.output_data_directory import OutputDataDirectory from rubem.configuration.output_format import OutputFileFormat +from rubem.configuration.output_raster_base import OutputRasterBase from rubem.configuration.output_variables import OutputVariables from rubem.configuration.raster_grid_area import RasterGrid from rubem.configuration.simulation_period import SimulationPeriod @@ -160,6 +161,7 @@ def __init__( kc_max=self.__get_setting("TABLES", "k_c_max"), validate_input=validate_input, ) + self.output_raster_base = OutputRasterBase(base_raster_path=self.raster_files.dem) except Exception as e: self.logger.error("Failed to load configuration: %s", e) raise diff --git a/rubem/configuration/output_raster_base.py b/rubem/configuration/output_raster_base.py new file mode 100644 index 0000000..3046fd2 --- /dev/null +++ b/rubem/configuration/output_raster_base.py @@ -0,0 +1,15 @@ +import logging +import os +from typing import Union + +from rubem.configuration.raster_map import RasterMap + + +class OutputRasterBase: + def __init__(self, base_raster_path: Union[str, bytes, os.PathLike]): + self.logger = logging.getLogger(__name__) + base_raster = RasterMap(base_raster_path) + self.cols = base_raster.raster.RasterXSize + self.rows = base_raster.raster.RasterYSize + self.transformation = base_raster.raster.GetGeoTransform() + base_raster = None diff --git a/rubem/file/_file_generators.py b/rubem/file/_file_generators.py index 4793699..d4b8bc1 100644 --- a/rubem/file/_file_generators.py +++ b/rubem/file/_file_generators.py @@ -1,109 +1,93 @@ -# coding=utf-8 -# RUBEM is a distributed hydrological model to calculate monthly -# flows with changes in land use over time. -# Copyright (C) 2020-2024 LabSid PHA EPUSP - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Contact: rubem.hydrological@labsid.eng.br - -"""Common file generation functionality used by RUBEM""" - import logging +import os +from typing import Optional, Union -from osgeo.gdal import AllRegister, GDT_Float32, OpenEx, UseExceptions +from osgeo import gdal +import pcraster as pcr from pcraster.framework import pcr2numpy -logger = logging.getLogger(__name__) - - -UseExceptions() +from rubem.configuration.output_format import OutputFileFormat +from rubem.configuration.output_raster_base import OutputRasterBase +logger = logging.getLogger(__name__) -def getRefInfo(sourceTif): - """Return size, resolution and corner coordinates from a\ - Raster file (DEM in RUBEM). - - :param sourceTif: Path to DEM file to get information - :sourceTif type: str - - :returns: Array with information - :rtype: array - """ - AllRegister() - ds = OpenEx(sourceTif) - cols = ds.RasterXSize - rows = ds.RasterYSize - trans = ds.GetGeoTransform() - driver = ds.GetDriver() - Ref = [cols, rows, trans, driver] - return Ref +gdal.UseExceptions() +gdal.AllRegister() -def reportTIFFSeries( - self, tifRef, pcrObj, fileName, outpath, currentStep, dyn=False +def report( + variable: pcr._pcraster.Field, + name: str, + outpath: Union[str, bytes, os.PathLike], + base_raster_info: OutputRasterBase, + timestep: Optional[int] = None, + format: OutputFileFormat = OutputFileFormat.GEOTIFF, + no_data_value: float = -9999, ): - """Create a .tif raster from a PCRaster object. + """Storing map data to disk using GDAL - :param tifRef: array with dem raster information - :tifRef type: array + :param variable: Variable containing the PCRaster map data + :type variable: pcr._pcraster.Field - :param pcrObj: PCRaster object to export. - :pcrObj type: Either Boolean, Nominal, Ordinal, Scalar, Directional or Ldd + :param timestep: Current timestep. If set the filename will contain the timestep (dynamic mode). Default is ``None``. + :type timestep: int, optional - :param fileName: Base name of the output file. - :fileName type: str + :param outpath: Path to store the output + :type outpath: Union[str, bytes, os.PathLike] - :param outpath: Path of the output directory. - :outpath type: str + :param name: Name used as filename. Use a filename with less than eight characters and without extension. File extension will be added automatically. + :type name: str - :param currentStep: Current model simulation step. - :outpath type: int + :param format: Output file format. Default is ``OutputFileFormat.GEOTIFF``. + :type format: OutputFileFormat, optional - :param dyn: If dynamic mode is True, otherwise defaults to False. - :dyn type: int + :param base_raster_info: Base raster information + :type base_raster_info: OutputRasterBase - :returns: File in .tif format - :rtype: .tif + :param no_data_value: No data value. Default is ``-9999``. + :type no_data_value: float, optional """ + if format == OutputFileFormat.GEOTIFF: + __report( + variable=variable, + timestep=timestep, + outpath=outpath, + name=name, + driver_short_name="GTiff", + extension="tif", + base_raster_info=base_raster_info, + no_data_value=no_data_value, + ) - # convert to np array - npFile = pcr2numpy(pcrObj, -999) - # generate file name - if not dyn: - out_tif = str(outpath + "/" + fileName + ".tif") - if dyn: - digits = 10 - len(fileName) - out_tif = str( - outpath + "/" + fileName + str(currentStep).zfill(digits) + ".tif" +def __report( + variable: pcr._pcraster.Field, + outpath: Union[str, bytes, os.PathLike], + name: str, + driver_short_name: str, + extension: str, + base_raster_info: OutputRasterBase, + timestep: Optional[int] = None, + no_data_value: float = -9999, +): + if timestep: + out_tif = os.path.join( + str(outpath), + f"{name}{str(timestep).zfill(10 - len(name))}.{extension}", ) - - # initialize export - cols = tifRef[0] - rows = tifRef[1] - trans = tifRef[2] - driver = tifRef[3] - - # create the output image - outDs = driver.Create( - out_tif, cols, rows, 1, GDT_Float32, options=["COMPRESS=LZW"] - ) - outBand = outDs.GetRasterBand(1) - outBand.SetNoDataValue(-9999) - outBand.WriteArray(npFile) - outDs.SetGeoTransform(trans) - ds = None - outDs = None + else: + out_tif = os.path.join(str(outpath), f"{name}.{extension}") + + with gdal.GetDriverByName(driver_short_name).Create( + out_tif, + base_raster_info.cols, + base_raster_info.rows, + bands=1, + eType=gdal.GDT_Float32, + options=["COMPRESS=LZW"], + ) as dataset: + band = dataset.GetRasterBand(1) + band.SetNoDataValue(no_data_value) + band.WriteArray(pcr2numpy(variable, no_data_value)) + dataset.SetGeoTransform(base_raster_info.transformation) From 223bfb120e50a05049483876b65299debc063826 Mon Sep 17 00:00:00 2001 From: Gabriel Soares <70075435+soaressgabriel@users.noreply.github.com> Date: Fri, 23 Feb 2024 20:51:55 -0300 Subject: [PATCH 2/2] Remove occurrences from the DEM raster in TIFF format --- doc/source/fileformats.rst | 19 ------------------- doc/source/tutorials.rst | 6 ------ doc/source/userguide.rst | 14 +------------- rubem/configuration/input_raster_files.py | 13 ------------- rubem/configuration/model_configuration.py | 1 - rubem/validation/_schemas.py | 2 -- tests/fixtures/base.template | 1 - tests/fixtures/base2.template | 1 - tests/fixtures/base3.template | 1 - tests/fixtures/base4.template | 1 - tests/fixtures/base7.template | 1 - tests/fixtures/base8.template | 1 - .../configuration/test_model_configuration.py | 3 --- 13 files changed, 1 insertion(+), 63 deletions(-) diff --git a/doc/source/fileformats.rst b/doc/source/fileformats.rst index 0dc8ee3..ef78948 100644 --- a/doc/source/fileformats.rst +++ b/doc/source/fileformats.rst @@ -46,25 +46,6 @@ This file is the result of pre-processing the corresponding TIFF/GeoTIFF raster - Columns = :ref:`clone columns`; - Cell Size = :ref:`clone cell size`. -Digital Elevation Map (DEM) raster (TIFF) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- Filetype: TIFF :file:`*.tif` raster file. -- Unit: `Meters Above Sea Level (MASL) `_ -- Valid Range: :math:`[-100.0, 10000.0]` -- Restrictions: - - - ``PCRASTER_VALUESCALE`` = ``VS_SCALAR``; - - None of the pixels in the raster must contain ``NO_DATA`` value; - - Raster pixels cannot consist entirely of ``1.0`` values; - - Raster pixels cannot consist entirely of ``0.0`` values. - -- Dimensions: - - - Rows = :ref:`clone rows `; - - Columns = :ref:`clone columns`; - - Cell Size = :ref:`clone cell size`. - Local Drain Direction (LDD) raster ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/source/tutorials.rst b/doc/source/tutorials.rst index 6f02c15..020fd53 100644 --- a/doc/source/tutorials.rst +++ b/doc/source/tutorials.rst @@ -67,7 +67,6 @@ In the part ``Grid`` set 500.000 m as size value and in the part Simulation Peri [RASTERS] dem = /Iguazu/input/maps/dem/dem.map - demtif = /Iguazu/input/maps/dem/dem.tif clone = /Iguazu/input/maps/clone/clone.map samples = /Iguazu/maps/stations/samples.map @@ -109,7 +108,6 @@ Set the following values for ``Initial Soil Conditions`` fields: [RASTERS] dem = /Iguazu/input/maps/dem/dem.map - demtif = /Iguazu/input/maps/dem/dem.tif clone = /Iguazu/input/maps/clone/clone.map samples = /Iguazu/maps/stations/samples.map soil = /Iguazu/input/maps/soil/soil.map @@ -174,7 +172,6 @@ Use the default values for ``FPAR``, ``LAI`` and ``Impervious Area Interception` [RASTERS] dem = /Iguazu/input/maps/dem/dem.map - demtif = /Iguazu/input/maps/dem/dem.tif clone = /Iguazu/input/maps/clone/clone.map samples = /Iguazu/maps/stations/samples.map soil = /Iguazu/input/maps/soil/soil.map @@ -243,7 +240,6 @@ In the ``Climate`` section define the appropriate map-series from :file:`/input/ [RASTERS] dem = /Iguazu/input/maps/dem/dem.map - demtif = /Iguazu/input/maps/dem/dem.tif clone = /Iguazu/input/maps/clone/clone.map samples = /Iguazu/maps/stations/samples.map soil = /Iguazu/input/maps/soil/soil.map @@ -335,7 +331,6 @@ Values in this tab correspond to calibrated parameters in the basin. For the dat [RASTERS] dem = /Iguazu/input/maps/dem/dem.map - demtif = /Iguazu/input/maps/dem/dem.tif clone = /Iguazu/input/maps/clone/clone.map samples = /Iguazu/maps/stations/samples.map soil = /Iguazu/input/maps/soil/soil.map @@ -420,7 +415,6 @@ The complete project configuration file should look like this: [RASTERS] dem = /Iguazu/input/maps/dem/dem.map - demtif = /Iguazu/input/maps/dem/dem.tif clone = /Iguazu/input/maps/clone/clone.map samples = /Iguazu/maps/stations/samples.map soil = /Iguazu/input/maps/soil/soil.map diff --git a/doc/source/userguide.rst b/doc/source/userguide.rst index 6550d63..fbac534 100644 --- a/doc/source/userguide.rst +++ b/doc/source/userguide.rst @@ -30,24 +30,13 @@ Mandatory path to the output folder. Must be a valid path to an existing **empty Digital Elevation Map (DEM) ``````````````````````````` -Mandatory path to Digital Elevation Map (DEM) file `[masl] `_ in: - - * **PCRaster map format** :file:`*.map`: this map contains topographic ground elevation in meters. Must be a valid file path to a PCRaster map format :file:`*.map` file. :ref:`See more. ` +Mandatory path to Digital Elevation Map (DEM) file `[masl] `_ in PCRaster map format :file:`*.map`: this map contains topographic ground elevation in meters. Must be a valid file path to a PCRaster map format :file:`*.map` file. :ref:`See more. ` .. code-block:: dosini [RASTERS] dem = /Dataset/UIGCRB/input/maps/dem/dem.map -------- - - * **TIF format** :file:`*.tif`: this map contains topographic ground elevation in meters. Must be a valid file path to a TIF :file:`*.tif` raster file. :ref:`See more. ` - - .. code-block:: dosini - - [RASTERS] - demtif = /Dataset/UIGCRB/input/maps/dem/dem.tif - Mask of Catchment (Clone) `````````````````````````` @@ -808,7 +797,6 @@ Configuration File Template [RASTERS] dem = /Dataset/UIRB/input/maps/dem/dem.map - demtif = /Dataset/UIRB/input/maps/dem/dem.tif clone = /Dataset/UIRB/input/maps/clone/clone.map ndvi_max = /Dataset/UIRB/input/maps/ndvi/ndvi_max.map ndvi_min = /Dataset/UIRB/input/maps/ndvi/ndvi_min.map diff --git a/rubem/configuration/input_raster_files.py b/rubem/configuration/input_raster_files.py index 6d1eee8..529a8b4 100644 --- a/rubem/configuration/input_raster_files.py +++ b/rubem/configuration/input_raster_files.py @@ -15,9 +15,6 @@ class InputRasterFiles: :param dem: Path to the DEM file (*.map format). :type dem: Union[str, bytes, os.PathLike] - :param demtif: Path to the DEM file (*.tif format). - :type demtif: Union[str, bytes, os.PathLike] - :param clone: Path to the mask of catchment (clone) file. :type clone: Union[str, bytes, os.PathLike] @@ -46,7 +43,6 @@ class InputRasterFiles: def __init__( self, dem: Union[str, bytes, os.PathLike], - demtif: Union[str, bytes, os.PathLike], clone: Union[str, bytes, os.PathLike], ndvi_max: Union[str, bytes, os.PathLike], ndvi_min: Union[str, bytes, os.PathLike], @@ -59,7 +55,6 @@ def __init__( self.__ranges = DataRangesSettings() self.dem = dem - self.demtif = demtif self.clone = clone self.ndvi_max = ndvi_max self.ndvi_min = ndvi_min @@ -81,13 +76,6 @@ def __validate_files(self) -> None: | RasterDataRules.FORBID_ALL_ZEROES | RasterDataRules.FORBID_ALL_ONES, ), - ( - self.demtif, - self.__ranges.rasters["dem"], - RasterDataRules.FORBID_NO_DATA - | RasterDataRules.FORBID_ALL_ZEROES - | RasterDataRules.FORBID_ALL_ONES, - ), (self.clone, self.__ranges.rasters["clone"], RasterDataRules.FORBID_ALL_ZEROES), (self.ndvi_max, self.__ranges.rasters["ndvi"], RasterDataRules.FORBID_NO_DATA), (self.ndvi_min, self.__ranges.rasters["ndvi"], RasterDataRules.FORBID_NO_DATA), @@ -131,7 +119,6 @@ def __validate_files(self) -> None: def __str__(self) -> str: return ( f"DEM (PCRaster Map): {self.dem}\n" - f"DEM (GeoTIFF Map): {self.demtif}\n" f"Mask of Catchment (Clone): {self.clone}\n" f"Local Drain Direction (LDD): {self.ldd if self.ldd else 'Not specified.'}\n" f"NDVI Max.: {self.ndvi_max}\n" diff --git a/rubem/configuration/model_configuration.py b/rubem/configuration/model_configuration.py index 556e1c9..b75ab93 100644 --- a/rubem/configuration/model_configuration.py +++ b/rubem/configuration/model_configuration.py @@ -135,7 +135,6 @@ def __init__( ) self.raster_files = InputRasterFiles( dem=self.__get_setting("RASTERS", "dem"), - demtif=self.__get_setting("RASTERS", "demtif"), clone=self.__get_setting("RASTERS", "clone"), ndvi_max=self.__get_setting("RASTERS", "ndvi_max"), ndvi_min=self.__get_setting("RASTERS", "ndvi_min"), diff --git a/rubem/validation/_schemas.py b/rubem/validation/_schemas.py index 7a2631b..f194fb5 100644 --- a/rubem/validation/_schemas.py +++ b/rubem/validation/_schemas.py @@ -40,7 +40,6 @@ }, "RASTERS": { "dem": "None", - "demtif": "None", "clone": "None", "ndvi_max": "None", "ndvi_min": "None", @@ -124,7 +123,6 @@ }, "RASTERS": { "dem": "None", - "demtif": "None", "clone": "None", "ndvi_max": "None", "ndvi_min": "None", diff --git a/tests/fixtures/base.template b/tests/fixtures/base.template index fa77024..ec87b86 100644 --- a/tests/fixtures/base.template +++ b/tests/fixtures/base.template @@ -20,7 +20,6 @@ landuse_prefix = cob [RASTERS] dem = {PARENT_DIR}/fixtures/base/maps/dem/dem.map -demtif = {PARENT_DIR}/fixtures/base/maps/dem/dem.tif clone = {PARENT_DIR}/fixtures/base/maps/clone/clone.map ndvi_max = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_max.map ndvi_min = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_min.map diff --git a/tests/fixtures/base2.template b/tests/fixtures/base2.template index fa77024..ec87b86 100644 --- a/tests/fixtures/base2.template +++ b/tests/fixtures/base2.template @@ -20,7 +20,6 @@ landuse_prefix = cob [RASTERS] dem = {PARENT_DIR}/fixtures/base/maps/dem/dem.map -demtif = {PARENT_DIR}/fixtures/base/maps/dem/dem.tif clone = {PARENT_DIR}/fixtures/base/maps/clone/clone.map ndvi_max = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_max.map ndvi_min = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_min.map diff --git a/tests/fixtures/base3.template b/tests/fixtures/base3.template index a8fe3fa..5e57d8b 100644 --- a/tests/fixtures/base3.template +++ b/tests/fixtures/base3.template @@ -20,7 +20,6 @@ landuse_prefix = cob [RASTERS] dem = {PARENT_DIR}/fixtures/base/maps/dem/dem.map -demtif = {PARENT_DIR}/fixtures/base/maps/dem/dem.tif clone = {PARENT_DIR}/fixtures/base/maps/clone/clone.map ndvi_max = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_max.map ndvi_min = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_min.map diff --git a/tests/fixtures/base4.template b/tests/fixtures/base4.template index 3097660..e711c7a 100644 --- a/tests/fixtures/base4.template +++ b/tests/fixtures/base4.template @@ -1,6 +1,5 @@ [RASTERS] dem = {PARENT_DIR}/fixtures/base/maps/dem/dem.map -demtif = {PARENT_DIR}/fixtures/base/maps/dem/dem.tif clone = {PARENT_DIR}/fixtures/base/maps/clone/clone.map ndvi_max = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_max.map ndvi_min = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_min.map diff --git a/tests/fixtures/base7.template b/tests/fixtures/base7.template index b4b2eaa..fc96813 100644 --- a/tests/fixtures/base7.template +++ b/tests/fixtures/base7.template @@ -1,6 +1,5 @@ [RASTERS] dem = {PARENT_DIR}/fixtures/base/maps/dem/dem.mapp -demtif = {PARENT_DIR}/fixtures/base/maps/dem/dem.tif clone = {PARENT_DIR}/fixtures/base/maps/clone/clone.map ndvi_max = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_max.map ndvi_min = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_min.map diff --git a/tests/fixtures/base8.template b/tests/fixtures/base8.template index 445239e..78f3fb6 100644 --- a/tests/fixtures/base8.template +++ b/tests/fixtures/base8.template @@ -1,6 +1,5 @@ [RASTERS] dem = {PARENT_DIR}/fixtures/base/maps/dem/dem.map -demtif = {PARENT_DIR}/fixtures/base/maps/dem/dem.tif clone = {PARENT_DIR}/fixtures/base/maps/clone/clone.map ndvi_max = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_max.map ndvi_min = {PARENT_DIR}/fixtures/base/maps/ndvi/ndvi_min.map diff --git a/tests/unit/configuration/test_model_configuration.py b/tests/unit/configuration/test_model_configuration.py index 68658ab..b5a8497 100644 --- a/tests/unit/configuration/test_model_configuration.py +++ b/tests/unit/configuration/test_model_configuration.py @@ -28,7 +28,6 @@ class TestModelConfiguration: }, "RASTERS": { "dem": "test_path/test_file.map", - "demtif": "test_path/test_file.tif", "clone": "test_path/test_file.map", "ndvi_max": "test_path/test_file.map", "ndvi_min": "test_path/test_file.map", @@ -103,7 +102,6 @@ class TestModelConfiguration: landuse_prefix = cob [RASTERS] dem = /test_path/test_file.map - demtif = /test_path/test_file.tif clone = /test_path/test_file.map ndvi_max = /test_path/test_file.map ndvi_min = /test_path/test_file.map @@ -184,7 +182,6 @@ class TestModelConfiguration: }, "RASTERS": { "dem": "test_path/test_file.map", - "demtif": "test_path/test_file.tif", "clone": "test_path/test_file.map", "ndvi_max": "test_path/test_file.map", "ndvi_min": "test_path/test_file.map",