diff --git a/bin/enpt_cli.py b/bin/enpt_cli.py index 2e345d9289c7d19a2b832cf7c34e9620b513dd6e..261faeb045082def47c4a1a23cb0fb02b390e35f 100644 --- a/bin/enpt_cli.py +++ b/bin/enpt_cli.py @@ -35,8 +35,12 @@ def get_enpt_argparser(): help='number of CPU cores to be used for processing (default: "None" -> use all available') add('-im', '--path_l1b_enmap_image', default=None, help='input path of the EnMAP L1B image to be processed (zip-archive or root directory)') + add('-od', '--output_dir', default=None, + help='output directory where processed data and log files are saved') add('-imgap', '--path_l1b_enmap_image_gapfill', default=None, help='input path of an adjacent EnMAP L1B image to be used for gap-filling (zip-archive or root directory)') + add('-wd', '--working_dir', default=None, + help='directory to be used for temporary files') # link parser to run function parser.set_defaults(func=run_job) @@ -78,7 +82,8 @@ def get_config(cli_args: argparse.Namespace): def run_job(config: EnPTConfig): - EnPT_Controller(config) + CTR = EnPT_Controller(config) + CTR.run_all_processors() if __name__ == '__main__': diff --git a/enpt/execution/controller.py b/enpt/execution/controller.py index 08924f085bc0cdadf847d79b7c2a07a0255a0f58..d8d051bf967e4296d0cb4bc7d43843d9eafa1e41 100644 --- a/enpt/execution/controller.py +++ b/enpt/execution/controller.py @@ -29,7 +29,7 @@ class EnPT_Controller(object): # generate temporary directory (must be deleted at the end of the pipeline) self.tempDir = tempfile.mkdtemp() - # setup a finalizer that destroys remaining data in case of unexpected exit + # setup a finalizer that destroys remaining data (directories, etc.) in case of unexpected exit self._finalizer = weakref.finalize(self, self._cleanup, self.tempDir, warn_message="Implicitly cleaning up {!r}".format(self)) @@ -83,11 +83,16 @@ class EnPT_Controller(object): """Run atmospheric correction only.""" pass + def write_output(self): + if self.cfg.output_dir: + self.L1_obj.save(self.cfg.output_dir) + def run_all_processors(self): """Run all processors at once.""" try: self.run_toaRad2toaRef() self.run_atmospheric_correction() + self.write_output() finally: self.cleanup() diff --git a/enpt/io/reader.py b/enpt/io/reader.py index f537d146e8ed1c15818d0f976179b448e854cbdf..54ad8ca59ade294611027a31e94d73b4c5cfc6a5 100644 --- a/enpt/io/reader.py +++ b/enpt/io/reader.py @@ -17,7 +17,12 @@ class L1B_Reader(object): """Reader for EnMAP Level-1B products.""" def __init__(self, config: EnPTConfig, logger: logging.Logger=None): - """Get an instance of L1B_Reader.""" + """Get an instance of L1B_Reader. + + :param config: instance of EnPTConfig class + :param logger: instance of logging.Logger (NOTE: This logger is only used to log messages within L1B_Reader. + It is not appended to the read L1B EnMAP object). + """ self.cfg = config self.logger = logger or logging.getLogger(__name__) @@ -26,17 +31,18 @@ class L1B_Reader(object): # TODO move to init? """Read L1B, DEM and spatial reference data. - :param root_dir: Root directory of EnMAP Level-1B product - :param observation_time: datetime of observation time (currently missing in metadata) - :param lon_lat_smpl: number if sampling points in lon, lat fields - :param nsmile_coef: number of polynomial coefficients for smile - :return: instance of EnMAPL1Product_MapGeo + :param root_dir: Root directory of EnMAP Level-1B product + :param observation_time: datetime of observation time (currently missing in metadata) + :param lon_lat_smpl: number if sampling points in lon, lat fields + :param nsmile_coef: number of polynomial coefficients for smile + :param compute_snr: whether to compute SNR or not (default: True) + :return: instance of EnMAPL1Product_MapGeo """ # get a new instance of EnMAPL1Product_MapGeo L1_obj = EnMAPL1Product_SensorGeo(root_dir, config=self.cfg) # read metadata - L1_obj.meta = EnMAP_Metadata_L1B_SensorGeo(L1_obj.paths.metaxml, config=self.cfg) + L1_obj.meta = EnMAP_Metadata_L1B_SensorGeo(L1_obj.paths.metaxml, config=self.cfg, logger=L1_obj.logger) L1_obj.meta.read_metadata(observation_time=observation_time, lon_lat_smpl=lon_lat_smpl, nsmile_coef=nsmile_coef) # read VNIR data @@ -58,7 +64,8 @@ class L1B_Reader(object): # compute SNR if compute_snr: - with tempfile.TemporaryDirectory() as tmpDir, zipfile.ZipFile(self.cfg.path_l1b_snr_model, "r") as zf: + with tempfile.TemporaryDirectory(dir=self.cfg.working_dir) as tmpDir,\ + zipfile.ZipFile(self.cfg.path_l1b_snr_model, "r") as zf: zf.extractall(tmpDir) if L1_obj.meta.vnir.unitcode == 'TOARad': diff --git a/enpt/model/images.py b/enpt/model/images.py index 332e6b216c9b706cf24ad7d661a5695bfc2a83bd..a6551ed3ed9c69f021903050759f2bbd7c8afd8a 100644 --- a/enpt/model/images.py +++ b/enpt/model/images.py @@ -43,15 +43,10 @@ class _EnMAP_Image(object): super(EnMAP_Image_Subclass, self).__init__() """ - # get all attributes of base class "Dataset" - super(_EnMAP_Image, self).__init__() - # add EnMAP specific attributes self.paths = SimpleNamespace() # protected attributes - self._logger = None - self._log = '' self._arr = None self._mask_nodata = None self._mask_clouds = None @@ -65,55 +60,6 @@ class _EnMAP_Image(object): self.entity_ID = '' self.basename = '' - @property - def logger(self): - """Get a an instance of enpt.utils.logging.EnPT_Logger. - - NOTE: - - The logging level will be set according to the user inputs of EnPT. - - The path of the log file is directly derived from the attributes of the _EnMAP_Image instance. - - Usage: - - get the logger: - logger = self.logger - - set the logger - self.logger = logging.getLogger() # NOTE: only instances of logging.Logger are allowed here - - delete the logger: - del self.logger # or "self.logger = None" - - :return: EnPT_Logger instance - """ - if self._logger and self._logger.handlers[:]: - return self._logger - else: - self._logger = EnPT_Logger('log__' + self.basename, fmt_suffix=None, path_logfile='', - log_level='INFO', append=True) # TODO revise the logger - - return self._logger - - @logger.setter - def logger(self, logger: logging.Logger): - assert isinstance(logger, logging.Logger) or logger in ['not set', None], \ - "_EnMAP_Image.logger can not be set to %s." % logger - - # save prior logs - # if logger is None and self._logger is not None: - # self.log += self.logger.captured_stream - self._logger = logger - - @property # FIXME does not work yet - def log(self): - """Return a string of all logged messages until now. - - NOTE: self.log can also be set to a string. - """ - return self._log - - @log.setter - def log(self, string): - assert isinstance(string, str), "'log' can only be set to a string. Got %s." % type(string) - self._log = string - @property def data(self): """Return the actual EnMAP image data. @@ -371,7 +317,8 @@ class EnMAP_Detector_SensorGeo(_EnMAP_Image): super(EnMAP_Detector_SensorGeo, self).__init__() self.paths = self.get_paths() # instance an empty metadata object - self.detector_meta = EnMAP_Metadata_L1B_Detector_SensorGeo(self.detector_name, config=self.cfg, logger=logger) + self.detector_meta = \ + EnMAP_Metadata_L1B_Detector_SensorGeo(self.detector_name, config=self.cfg, logger=self.logger) def get_paths(self): """Get all file paths associated with the current instance of _EnMAP_Detector_SensorGeo. @@ -416,7 +363,7 @@ class EnMAPL1Product_SensorGeo(object): Attributes: - logger: - - logging.Logger instance or subclassed + - logging.Logger instance or subclass instance - vnir - instance of EnMAP_VNIR_SensorGeo class - swir @@ -433,17 +380,75 @@ class EnMAPL1Product_SensorGeo(object): def __init__(self, root_dir: str, config: EnPTConfig, logger=None): """Get instance of EnPT EnMAP object in sensor geometry. - :param root_dir: Root directory of EnMAP Level-1B product - :param logger: None or logging instance + :param root_dir: Root directory of EnMAP Level-1B product + :param logger: None or logging instance to be appended to EnMAPL1Product_SensorGeo instance + (If None, a default EnPT_Logger is used). """ + # protected attributes + self._logger = None + + # populate attributes self.cfg = config - self.logger = logger or logging.getLogger(__name__) - self.vnir = EnMAP_Detector_SensorGeo('VNIR', root_dir, config=self.cfg, logger=logger) - self.swir = EnMAP_Detector_SensorGeo('SWIR', root_dir, config=self.cfg, logger=logger) + if logger: + self.logger = logger + self.vnir = EnMAP_Detector_SensorGeo('VNIR', root_dir, config=self.cfg, logger=self.logger) + self.swir = EnMAP_Detector_SensorGeo('SWIR', root_dir, config=self.cfg, logger=self.logger) self.paths = self.get_paths() - self.meta = EnMAP_Metadata_L1B_SensorGeo(self.paths.metaxml, config=self.cfg, logger=logger) + self.meta = EnMAP_Metadata_L1B_SensorGeo(self.paths.metaxml, config=self.cfg, logger=self.logger) self.detector_attrNames = ['vnir', 'swir'] + @property + def logger(self) -> EnPT_Logger: + """Get a an instance of enpt.utils.logging.EnPT_Logger. + + NOTE: + - The logging level will be set according to the user inputs of EnPT. + - The path of the log file is directly derived from the attributes of the _EnMAP_Image instance. + + Usage: + - get the logger: + logger = self.logger + - set the logger + self.logger = logging.getLogger() # NOTE: only instances of logging.Logger are allowed here + - delete the logger: + del self.logger # or "self.logger = None" + + :return: EnPT_Logger instance + """ + if self._logger and self._logger.handlers[:]: + return self._logger + else: + basename = path.splitext(path.basename(self.cfg.path_l1b_enmap_image))[0] + path_logfile = path.join(self.cfg.output_dir, basename + '.log') \ + if self.cfg.create_logfile and self.cfg.output_dir else '' + self._logger = EnPT_Logger('log__' + basename, fmt_suffix=None, path_logfile=path_logfile, + log_level=self.cfg.log_level, append=False) + + return self._logger + + @logger.setter + def logger(self, logger: logging.Logger): + assert isinstance(logger, logging.Logger) or logger in ['not set', None], \ + "%s.logger can not be set to %s." % (self.__class__.__name__, logger) + + # save prior logs + # if logger is None and self._logger is not None: + # self.log += self.logger.captured_stream + self._logger = logger + + @property + def log(self) -> str: + """Return a string of all logged messages until now. + + NOTE: self.log can also be set to a string. + """ + return self.logger.captured_stream + + @log.setter + def log(self, string: str): + assert isinstance(string, str), "'log' can only be set to a string. Got %s." % type(string) + self.logger.captured_stream = string + def get_paths(self): """Get all file paths associated with the current instance of EnMAPL1Product_SensorGeo. @@ -470,12 +475,12 @@ class EnMAPL1Product_SensorGeo(object): def save(self, outdir: str, suffix="") -> str: """Save this product to disk using almost the same format as for reading. - :param outdir: Path to output directory + :param outdir: Path to output directory + :param suffix: Suffix to be appended to the output filename :return: Root path of written product """ product_dir = path.join( - path.abspath(outdir), - "{name}{suffix}".format( + path.abspath(outdir), "{name}{suffix}".format( name=[ff for ff in self.paths.root_dir.split(sep) if ff != ''][-1], suffix=suffix) ) diff --git a/enpt/model/metadata.py b/enpt/model/metadata.py index c78a24a7a59bbb387cb56ae747eeb42c71e1ea28..cb32a0a66060a98a632a0c662fa94427adce264d 100644 --- a/enpt/model/metadata.py +++ b/enpt/model/metadata.py @@ -82,7 +82,7 @@ class EnMAP_Metadata_L1B_Detector_SensorGeo(object): """ xml = ElementTree.parse(path_xml).getroot() lbl = self.detector_label - self.logger.info("Load data for: %s" % lbl) + self.logger.info("Reading metadata for %s detector..." % self.detector_name) self.fwhm = np.array(xml.findall("%s/fwhm" % lbl)[0].text.replace("\n", "").split(), dtype=np.float) self.wvl_center = np.array( @@ -164,7 +164,7 @@ class EnMAP_Metadata_L1B_Detector_SensorGeo(object): rad_data = np.array(rad_data) assert self.unitcode == 'TOARad' - self.logger.info("Computing SNR for: %s using: %s" % (self.detector_name, path_snr_model)) + self.logger.info("Computing SNR for %s using %s" % (self.detector_name, path_snr_model)) if self.detector_name == 'VNIR': gA = sp.open_image(path_snr_model) diff --git a/enpt/options/config.py b/enpt/options/config.py index 7f803819a53b2eb93e79c9cf17c2cc2b860d78fa..c20b9ba390401a0b4404a39cd33a5535e6fcc566 100644 --- a/enpt/options/config.py +++ b/enpt/options/config.py @@ -62,9 +62,17 @@ class EnPTConfig(object): self.CPUs = gp('CPUs', fallback=cpu_count()) self.log_level = gp('log_level') + self.create_logfile = gp('create_logfile') self.path_l1b_enmap_image = self.absPath(gp('path_l1b_enmap_image')) self.path_l1b_enmap_image_gapfill = self.absPath(gp('path_l1b_enmap_image_gapfill')) self.path_l1b_snr_model = self.absPath(gp('path_l1b_snr_model')) + self.working_dir = self.absPath(gp('working_dir')) or None + + ################## + # output options # + ################## + + self.output_dir = self.absPath(gp('output_dir')) ########################### # processor configuration # diff --git a/enpt/options/options_default.json b/enpt/options/options_default.json index abb29394f87f5d63bef764bd2e7eb1a2987e070f..fc9476f6db2a32b55062851c298e7daeab10584f 100644 --- a/enpt/options/options_default.json +++ b/enpt/options/options_default.json @@ -2,9 +2,15 @@ "general_opts": { "CPUs": "None", /*number of CPU cores to be used for processing (default: "None" -> use all available)*/ "log_level": "INFO", /*the logging level to be used (choices: 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')*/ + "create_logfile": true, /*whether to write all log messages to a file (within output the directory)*/ "path_l1b_enmap_image": "", /*input path of the EnMAP L1B image to be processed (zip-archive or root directory)*/ "path_l1b_enmap_image_gapfill": "", /*input path of an adjacent EnMAP L1B image to be used for gap-filling (zip-archive or root directory)*/ - "path_l1b_snr_model": "./resources/EnMAP_Sensor/EnMAP_Level_1B_SNR.zip" /*input path of the EnMAP SNR model*/ + "path_l1b_snr_model": "./resources/EnMAP_Sensor/EnMAP_Level_1B_SNR.zip", /*input path of the EnMAP SNR model*/ + "working_dir": "" /*directory to be used for temporary files*/ + }, + + "output": { + "output_dir": "" /*output directory where processed data and log files are saved*/ }, "processors": { diff --git a/enpt/options/options_schema.py b/enpt/options/options_schema.py index a60b710424e6b08856cd7fc80a9bad9ac1e0f035..b10a7151040562a4d2d426d89a03fd4900cbb029 100644 --- a/enpt/options/options_schema.py +++ b/enpt/options/options_schema.py @@ -6,9 +6,17 @@ enpt_schema_input = dict( schema=dict( CPUs=dict(type='integer', required=False, nullable=True), log_level=dict(type='string', required=False, allowed=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']), + create_logfile=dict(type='boolean', required=False), path_l1b_enmap_image=dict(type='string', required=False), path_l1b_enmap_image_gapfill=dict(type='string', required=False), path_l1b_snr_model=dict(type='string', required=False), + working_dir=dict(type='string', required=False, nullable=True), + )), + + output=dict( + type='dict', required=False, + schema=dict( + output_dir=dict(type='string', required=False), )), processors=dict( @@ -62,9 +70,14 @@ parameter_mapping = dict( # general opts CPUs=('general_opts', 'CPUs'), log_level=('general_opts', 'log_level'), + create_logfile=('general_opts', 'create_logfile'), path_l1b_enmap_image=('general_opts', 'path_l1b_enmap_image'), path_l1b_enmap_image_gapfill=('general_opts', 'path_l1b_enmap_image_gapfill'), path_l1b_snr_model=('general_opts', 'path_l1b_snr_model'), + working_dir=('general_opts', 'working_dir'), + + # output + output_dir=('output', 'output_dir'), # processors > toa_ref path_earthSunDist=('processors', 'toa_ref', 'path_earthSunDist'), diff --git a/enpt/processors/radiometric_transform/radiometric_transform.py b/enpt/processors/radiometric_transform/radiometric_transform.py index 0fa22d8287788ec2a4549f108676916cf6adffc5..e23bf06ad6645c1b900f853aedcbb8be94553939 100644 --- a/enpt/processors/radiometric_transform/radiometric_transform.py +++ b/enpt/processors/radiometric_transform/radiometric_transform.py @@ -22,9 +22,7 @@ class Radiometric_Transformer(object): self.earthSunDist = config.path_earthSunDist # path of model for earth sun distance @staticmethod - def transform_TOARad2TOARef(enmap_ImageL1: EnMAPL1Product_SensorGeo, - scale_factor: int=10000 - ): + def transform_TOARad2TOARef(enmap_ImageL1: EnMAPL1Product_SensorGeo, scale_factor: int=10000): """Transform top-of-atmosphere radiance to top-of-atmosphere reflectance. NOTE: The following formula is used: @@ -39,7 +37,7 @@ class Radiometric_Transformer(object): detector = getattr(enmap_ImageL1, detectorName) # type: EnMAP_Detector_SensorGeo enmap_ImageL1.logger.info('Converting TOA radiance to TOA reflectance for %s detector...' - % detector.detector_name) # FIXME does not log anything + % detector.detector_name) # compute TOA reflectance constant = \ diff --git a/enpt/utils/logging.py b/enpt/utils/logging.py index 1af900a6b552a0f831aaa9a1d7944a6b2a58fdd6..90d351375e7c8fc27652fbc814e8593c140ed6c1 100644 --- a/enpt/utils/logging.py +++ b/enpt/utils/logging.py @@ -5,6 +5,7 @@ import logging import os import warnings import sys +from io import StringIO # Python 3 only class EnPT_Logger(logging.Logger): @@ -51,11 +52,11 @@ class EnPT_Logger(logging.Logger): else: fileHandler = None - # create StreamHandler # TODO add a StringIO handler - # self.streamObj = StringIO() - # self.streamHandler = logging.StreamHandler(stream=self.streamObj) - # self.streamHandler.setFormatter(formatter) - # self.streamHandler.set_name('StringIO handler') + # create StreamHandler + self.streamObj = StringIO() + self.streamHandler = logging.StreamHandler(stream=self.streamObj) + self.streamHandler.setFormatter(self.formatter_fileH) + self.streamHandler.set_name('StringIO handler') # create ConsoleHandler for logging levels DEGUG and INFO -> logging to sys.stdout consoleHandler_out = logging.StreamHandler(stream=sys.stdout) # by default it would go to sys.stderr @@ -75,37 +76,28 @@ class EnPT_Logger(logging.Logger): if not self.handlers: if fileHandler: self.addHandler(fileHandler) - # self.addHandler(self.streamHandler) + self.addHandler(self.streamHandler) self.addHandler(consoleHandler_out) self.addHandler(consoleHandler_err) - # if append: - # logfileHandler = logging.FileHandler(path_logfile, mode='a') - # else: - # logfileHandler = logging.FileHandler(path_logfile, mode='w') - # logfileHandler.setFormatter(formatter) - # logfileHandler.setLevel(logging.CRITICAL) - # consoleHandler_out = logging.StreamHandler() - # consoleHandler_out.setFormatter(formatter) - # consoleHandler_out.setLevel(logging.CRITICAL) - # # logger.setLevel(logging.DEBUG) - # if CPUs == 1: - # if not logger.handlers: - # logger.addHandler(logfileHandler) - # logger.addHandler(consoleHandler_out) - # else: - # logger.addHandler(logfileHandler) - # logger.addHandler(consoleHandler_out) + def __getstate__(self): + self.close() + return self.__dict__ + + def __setstate__(self, ObjDict): + """Defines how the attributes of EnPT_Logger are unpickled.""" + self.__init__(ObjDict['name_logfile'], fmt_suffix=ObjDict['fmt_suffix'], path_logfile=ObjDict['path_logfile'], + log_level=ObjDict['log_level'], append=True) + ObjDict = self.__dict__ + return ObjDict @property - def captured_stream(self): + def captured_stream(self) -> str: """Return the already captured logging stream. NOTE: - set self.captured_stream: self.captured_stream = 'any string' - - :return: str """ if not self._captured_stream: self._captured_stream = self.streamObj.getvalue() @@ -113,28 +105,23 @@ class EnPT_Logger(logging.Logger): return self._captured_stream @captured_stream.setter - def captured_stream(self, string): + def captured_stream(self, string: str): assert isinstance(string, str), "'captured_stream' can only be set to a string. Got %s." % type(string) self._captured_stream = string def close(self): """Close all logging handlers.""" # update captured_stream and flush stream - # self.captured_stream += self.streamObj.getvalue() - # print(self.handlers[:]) - - # self.removeHandler(self.streamHandler) - # print(dir(self.streamHandler)) - # self.streamHandler = None + self.captured_stream += self.streamObj.getvalue() for handler in self.handlers[:]: try: - # if handler.get_name() == 'StringIO handler': - # self.streamObj.flush() - # self.streamHandler.flush() - - handler.close() + if handler.get_name() == 'StringIO handler': + self.streamObj.flush() + self.streamHandler.flush() self.removeHandler(handler) # if not called with '[:]' the StreamHandlers are left open + # handler.flush() + handler.close() except PermissionError: warnings.warn('Could not properly close logfile due to a PermissionError: %s' % sys.exc_info()[1]) @@ -148,6 +135,16 @@ class EnPT_Logger(logging.Logger): with open(self.path_logfile) as inF: print(inF.read()) + def __del__(self): + self.close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + return True if exc_type is None else False + def close_logger(logger): """Close the handlers of the given logging.Logger instance. @@ -157,8 +154,9 @@ def close_logger(logger): if logger and hasattr(logger, 'handlers'): for handler in logger.handlers[:]: # if not called with '[:]' the StreamHandlers are left open try: - handler.close() logger.removeHandler(handler) + handler.flush() + handler.close() except PermissionError: warnings.warn('Could not properly close logfile due to a PermissionError: %s' % sys.exc_info()[1]) diff --git a/tests/__init__.py b/tests/__init__.py index 6b8e248cbb4972c9bd21b65e5ebe267a91522e34..60939742c5d817b679945cb617dd28742b832a1f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,5 +5,6 @@ import os enptRepo_rootpath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) config_for_testing = dict( - path_l1b_enmap_image=os.path.join(enptRepo_rootpath, 'tests', 'data', 'EnMAP_Level_1B', 'AlpineTest1_CWV2_SM0.zip') + path_l1b_enmap_image=os.path.join(enptRepo_rootpath, 'tests', 'data', 'EnMAP_Level_1B', 'AlpineTest1_CWV2_SM0.zip'), + output_dir=os.path.join(enptRepo_rootpath, 'tests', 'data', 'test_outputs') ) diff --git a/tests/test_controller.py b/tests/test_controller.py index 2aed8b90aa2270645715cde74a879fc926a7103f..195de5b1862e31c6ca344c7027da33282b44efae 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -8,6 +8,7 @@ Tests for `execution.controller` module. """ from unittest import TestCase +import shutil from enpt.execution.controller import EnPT_Controller @@ -18,5 +19,8 @@ class Test_EnPT_Controller(TestCase): def setUp(self): self.CTR = EnPT_Controller(**config_for_testing) + def tearDown(self): + shutil.rmtree(self.CTR.cfg.output_dir) + def test_run_all_processors(self): self.CTR.run_all_processors() diff --git a/tests/test_l1b_reader.py b/tests/test_l1b_reader.py index 921aafed87b18c7c0cee759b7e0a8cfbcb33cc64..62a60265519fa1a0e0d255ea6a5767f1ded1eebe 100644 --- a/tests/test_l1b_reader.py +++ b/tests/test_l1b_reader.py @@ -18,6 +18,7 @@ from datetime import datetime import shutil from enpt.options.config import EnPTConfig +from . import config_for_testing class Test_L1B_Reader(unittest.TestCase): @@ -25,12 +26,12 @@ class Test_L1B_Reader(unittest.TestCase): def setUp(self): self.pathList_testimages = glob(os.path.join(os.path.dirname(__file__), "data", "EnMAP_Level_1B", "*.zip")) - self.config = EnPTConfig() - - self.tmpdir = tempfile.mkdtemp() + self.config = EnPTConfig(**config_for_testing) + self.tmpdir = tempfile.mkdtemp(dir=self.config.working_dir) def tearDown(self): shutil.rmtree(self.tmpdir) + shutil.rmtree(self.config.output_dir) def test_read_and_save_inputdata(self): from enpt.io.reader import L1B_Reader @@ -72,7 +73,7 @@ class Test_L1B_Reader(unittest.TestCase): self.assertIsInstance(L1_obj, EnMAPL1Product_SensorGeo) self.assertIsNotNone(L1_obj.vnir.detector_meta.snr) self.assertIsNotNone(L1_obj.swir.detector_meta.snr) - root_dir_written_L1_data = L1_obj.save(path.join(self.tmpdir, "with_snr")) + root_dir_written_L1_data = L1_obj.save(path.join(self.config.output_dir, "with_snr")) # read self written L1 data L1_obj = rd.read_inputdata(root_dir_written_L1_data, observation_time=datetime(2015, 12, 7, 10))