From e0ca65e6b173eb6a5496350adc65737c845ba938 Mon Sep 17 00:00:00 2001 From: Luc Hermitte <luc.hermitte@cs-soprasteria.com> Date: Fri, 28 Feb 2025 19:14:08 +0100 Subject: [PATCH 1/5] DOC: Update release_notes about Python 3.8 EOL --- docs/release_notes.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 3c245b1c..4d7cc566 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -10,8 +10,12 @@ v1.2.0 Breaking changes +++++++++++++++++++++++ - Compatibility to OTB 7.x (and even 8.x) is no longer actively pursued. - S1Tiling may work with older version of OTB, but with no guarantees. + S1Tiling may work with older version of OTB, but with no guarantees (`#164 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/164>`_). +- Compatibility to Python 3.8 is no longer actively pursued as Python 3.8 has + reached its end-of-life in 2024. + S1Tiling may work with older version of Python, but with no guarantees + (`#158 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/158>`_). v1.2.0 Improvements +++++++++++++++++++ -- GitLab From 7d1b25e93fd16924b3df09df2342c86f9a50484c Mon Sep 17 00:00:00 2001 From: Luc Hermitte <luc.hermitte@cs-soprasteria.com> Date: Fri, 28 Feb 2025 22:11:56 +0100 Subject: [PATCH 2/5] ENH: Add extra GeoTIFF `[Metadata]` section in conf --- docs/configuration.rst | 8 ++++++++ docs/files.rst | 24 +++++++++++++++++------- docs/release_notes.rst | 17 ++++++++++------- s1tiling/libs/configuration.py | 26 +++++++++++++++++++++++--- s1tiling/libs/steps.py | 5 ++++- s1tiling/resources/S1Processor.cfg | 4 ++++ 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 0668ac3f..9303db9c 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -749,6 +749,14 @@ You can use this :download:`this template V1.0 of S1Tiling. See Issue `#118 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/118>`_. +.. _Metadata: + +``[Metadata]`` section +++++++++++++++++++++++ + +You can place in this section any extra ``key : value`` information that you +want written in the GeoTIFF metadata of S1Tiling products. + .. commented-out-to-be-implemented: .. _Filtering.reset_outcore: diff --git a/docs/files.rst b/docs/files.rst index e29fa63c..4c7e11b7 100644 --- a/docs/files.rst +++ b/docs/files.rst @@ -50,7 +50,11 @@ Orthorectified S2 tiles - defined in :ref:`[Processing].creation_options.tiled <processing.creation_options.tiled>` -:Metadata: The following metadata is added to the origin S1 images +:Metadata: + + GeoTIFF metadata will contain: the ones coming from the input S1 GRD + products, the extra ones specified in :ref:`[Metadata] <metadata>` + configuration section, plus the following ones: .. list-table:: :widths: auto @@ -122,7 +126,6 @@ Mask files - defined in :ref:`[Processing].creation_options.mask <processing.creation_options.mask>` - :Metadata: This file contains the same metadata as the one from :ref:`the S2 tile product <full-S2-tiles>` it has been generated from, with the following as the only difference: .. list-table:: @@ -168,7 +171,6 @@ Filtered files - defined in :ref:`[Processing].creation_options.filtered <processing.creation_options.filtered>` - :Metadata: This file contains the same metadata as the one from :ref:`the S2 tile product <full-S2-tiles>` it has been generated from, with the following as the only difference: .. list-table:: @@ -238,8 +240,11 @@ Local Incidence Angle map files (/:ref:`[Processing].creation_options.lia_deg <processing.creation_options.lia_deg>`) +:Metadata: -:Metadata: The following image metadata is set: + GeoTIFF metadata will contain the extra metadata specified in + :ref:`[Metadata] <metadata>` configuration section, plus the following + ones: .. list-table:: :widths: auto @@ -325,8 +330,11 @@ tile <full-S2-tiles>` from one calibration (β°, σ°, γ°) to another. (/:ref:`[Processing].creation_options.ia_deg <processing.creation_options.ia_deg>`) +:Metadata: -:Metadata: The following image metadata is set: + GeoTIFF metadata will contain the extra metadata specified in + :ref:`[Metadata] <metadata>` configuration section, plus the following + ones: .. list-table:: :widths: auto @@ -459,8 +467,10 @@ Orthorectified S1 images :Product encoding: Float32 GeoTIFF, deflate compressed -:Metadata: The metadata listed for :ref:`the S2 tile product <full-S2-tiles>` - are actually produced at this step. +:Metadata: + + The metadata listed for :ref:`the S2 tile product <full-S2-tiles>` are + actually produced at this step. .. note:: These files are automatically cleaned up. diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 4d7cc566..fe035259 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -24,15 +24,18 @@ v1.2.0 Improvements Incidence Angle maps <lia-files>` (`#151 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/151>`_). - Support eodag 3 - (`#170 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/170>`_), - (`#177 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/177>`_), - (`#178 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/178>`_). + (`#170 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/170>`_, + `#177 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/177>`_, + `#178 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/178>`_). An indirect consequence is that products will be downloaded into :samp:`{{s1images}}/{{product_name}}/` instead of :samp:`{{s1images}}/{{product_name}}/{{product_name}}.SAFE/`. The old output directory structure is still supported for backward compatibility reasons. - Generate :ref:`maps of incidence angles to the WGS84 ellipsoid <ia-files>` (`#161 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/161>`_). +- Add :ref:`[Metadata] <metadata>` configuration section to specify extra + GeoTIFF metadata that will be set in the images produced by S1Tiling + (`#171 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/171>`_). Version 1.1.0 @@ -75,7 +78,7 @@ v1.1.0 Improvements :ref:`creation_options.* <Processing.creation_options>` options (`#66 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/66>`_). - GEOID file is also copied alongside DEM data when :ref:`[Processing].cache_dem_by - <Processing.cache_dem_by>` option is on. + <Processing.cache_dem_by>` option is on (`#123 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/123>`_). v1.1.0 Bugs fixed @@ -83,12 +86,12 @@ v1.1.0 Bugs fixed - Noise correction post-processing shall not transform wide no-data sides from Sentinel-1 IPF 2.90+ products into :ref:`minimal signal value - <Processing.lower_signal_value>`. + <Processing.lower_signal_value>` (`#159 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/159>`_). -- Handling of `nodata` values has been improved. +- Handling of `nodata` values has been improved (`#159 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/159>`_, - (`#160 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/160>`_). + `#160 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/160>`_). v1.1.0 Breaking changes diff --git a/s1tiling/libs/configuration.py b/s1tiling/libs/configuration.py index b127d486..849a2c17 100644 --- a/s1tiling/libs/configuration.py +++ b/s1tiling/libs/configuration.py @@ -245,6 +245,15 @@ class _ConfigAccessor: """Helper function to report errors while extracting boolean configuration options""" return getboolean_opt(self.__config, self.config_file, section, name, **kwargs) + def get_items(self, section: str) -> Dict: + """Helper function to return configuration items from a section""" + res = {} + if self.__config.has_section(section): + options = self.__config.options(section) - self.__config.defaults().keys() + for option in options: + res[option] = self.__config.get(section, option, raw=True) + return res + # The configuration decoding specific to S1Tiling application class Configuration: # pylint: disable=too-many-instance-attributes @@ -271,6 +280,7 @@ class Configuration: # pylint: disable=too-many-instance-attributes self.__init_fname_fmt(accessor) self.__init_dname_fmt(accessor) self.__init_creation_options(accessor) + self.__init_extra_metadata(accessor) # Other options #: Type of images handled @@ -570,6 +580,13 @@ class Configuration: # pylint: disable=too-many-instance-attributes self.creation_options[key] = cos + # ---------------------------------------------------------------------- + def __init_extra_metadata(self, accessor: _ConfigAccessor) -> None: + # TODO: how can we handle metadata that don't always make sense like DEM kind... + # => take the directory of the DEM files, or the ID key or the .gpkg file, or a manual option + #: Extra geotiff metadata options to write in all products + self.extra_metadata = accessor.get_items('Metadata') + # ---------------------------------------------------------------------- def show_configuration(self) -> None: # pylint: disable=too-many-statements """ @@ -630,15 +647,18 @@ class Configuration: # pylint: disable=too-many-instance-attributes elif self.filter in ['frost']: logging.info("- deramp : %s", self.filter_options['deramp']) + logging.info('Extra metadata : %s', len(self.extra_metadata)) + for meta, value in self.extra_metadata.items(): + logging.info('- %s --> %s', meta, value) logging.info('Output directories:') for k, fmt in self.dname_fmt.items(): - logging.info(' - %s --> %s', k, fmt) + logging.info('- %s --> %s', k, fmt) logging.info('Filename formats:') for k, fmt in self.fname_fmt.items(): - logging.info(' - %s --> %s', k, fmt) + logging.info('- %s --> %s', k, fmt) logging.info('Creation options:') for k, co in self.creation_options.items(): - logging.info(' - %s --> %s', k, co) + logging.info('- %s --> %s', k, co) def init_logger(self, config_log_dir: Path, mode=None) -> None: """ diff --git a/s1tiling/libs/steps.py b/s1tiling/libs/steps.py index 58646a98..921d2576 100644 --- a/s1tiling/libs/steps.py +++ b/s1tiling/libs/steps.py @@ -574,6 +574,7 @@ class StepFactory(ABC): assert isinstance(name, str), f"{self.__class__.__name__} name is a {name.__class__.__name__}, not a string -> {name!r}" self._name = name self.__image_description = kwargs.get('image_description', None) + self.__extra_metadata = kwargs.get('extra_metadata', {}) # logger.debug("new StepFactory(%s)", name) @property @@ -748,6 +749,8 @@ class StepFactory(ABC): imd['TIFFTAG_IMAGEDESCRIPTION'] = self.image_description.format( **meta, flying_unit_code_short=meta.get('flying_unit_code', 'S1?')[1:].upper()) + for key, value in self.__extra_metadata.items(): + imd[key] = value def _get_inputs(self, previous_steps: List[InputList]) -> InputList: """ @@ -985,7 +988,7 @@ class _FileProducingStepFactory(StepFactory): :func:`build_step_output_tmp_filename` for the usage of ``gen_tmp_dir``, ``gen_output_dir`` and ``gen_output_filename``. """ - super().__init__(*argv, **kwargs) + super().__init__(*argv, extra_metadata=cfg.extra_metadata, **kwargs) is_a_final_step = gen_output_dir and gen_output_dir != gen_tmp_dir # logger.debug("%s -> final: %s <== gen_tmp=%s gen_out=%s", self.name, is_a_final_step, gen_tmp_dir, gen_output_dir) diff --git a/s1tiling/resources/S1Processor.cfg b/s1tiling/resources/S1Processor.cfg index 45d2a41f..3659ffec 100644 --- a/s1tiling/resources/S1Processor.cfg +++ b/s1tiling/resources/S1Processor.cfg @@ -259,3 +259,7 @@ keep_non_filtered_products : True # # If True, the outcore of the multiImage filter is reset before filtering. It means that the outcore is recomputed from scratch with the new images only. # # If False, the outcore is updated with the new images. Then, the outcore integrates previous images and new images. # reset_outcore : True + +[Metadata] +contact : Your Name <your.email@your.wo.rk> +any thing : you want added in the output image metadata -- GitLab From 97be55f296734c74fe7d31aac90959e73da2f478 Mon Sep 17 00:00:00 2001 From: Luc Hermitte <luc.hermitte@cs-soprasteria.com> Date: Sat, 1 Mar 2025 00:39:07 +0100 Subject: [PATCH 3/5] ENH: Write DEM/GEOID interp method in GeoTIFF meta --- docs/files.rst | 15 +++++++++++++-- s1tiling/libs/otbwrappers/lia.py | 21 +++++++++++++++++---- s1tiling/libs/otbwrappers/s1_to_s2.py | 7 ++++--- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/docs/files.rst b/docs/files.rst index 4c7e11b7..44c08b67 100644 --- a/docs/files.rst +++ b/docs/files.rst @@ -86,6 +86,9 @@ Orthorectified S2 tiles - :samp:`{{orbitNumber}}` * - ``ORBIT_DIRECTION`` - :samp:`{{orbitDirection}}` + * - ``ORTHORECTIFICATION_INTERPOLATOR`` + - :ref:`chosen orthorectification interpolation method option + <Processing.orthorectification_interpolation_method>` * - ``ORTHORECTIFIED`` - :samp:`true` * - ``POLARIZATION`` @@ -258,10 +261,15 @@ Local Incidence Angle map files - :samp:`100 * degrees(LIA)` / :samp:`sin(LIA)` * - ``DEM_LIST`` - List of DEM (SRTM currently) tiles used to generate the file + * - ``DEM_RESAMPLING_METHOD`` + - :ref:`chosen DEM resampling method option <Processing.dem_warp_resampling_method>` * - ``EOF_FILE`` - Precise orbit file used to generate the file * - ``FLYING_UNIT_CODE`` - :samp:`s1{{a|b}}` + * - ``GEOID_ORTHORECTIFICATION_INTERPOLATOR`` + - :ref:`chosen orthorectification interpolation_method option + <Processing.orthorectification_interpolation_method>` * - ``IMAGE_TYPE`` - :samp:`LIA` * - ``RELATIVE_ORBIT_NUMBER`` @@ -357,8 +365,6 @@ tile <full-S2-tiles>` from one calibration (β°, σ°, γ°) to another. - :samp:`true` * - ``RELATIVE_ORBIT_NUMBER`` - :samp:`{{orbitnumber}}` - * - ``ORTHORECTIFIED`` - - :samp:`true` * - ``S2_TILE_CORRESPONDING_CODE`` - :samp:`{{tilename}}` * - ``SPATIAL_RESOLUTION`` @@ -522,6 +528,8 @@ DEM data projected on S2 tile * - Metadata - Value + * - ``DEM_RESAMPLING_METHOD`` + - :ref:`chosen DEM resampling method option <Processing.dem_warp_resampling_method>` * - ``S2_TILE_CORRESPONDING_CODE`` - :samp:`{{tilename}}` * - ``SPATIAL_RESOLUTION`` @@ -556,6 +564,9 @@ Height (DEM+Geoid) projected on S2 tile * - Metadata - Value + * - ``GEOID_ORTHORECTIFICATION_INTERPOLATOR`` + - :ref:`chosen orthorectification interpolation_method option + <Processing.orthorectification_interpolation_method>` * - ``TIFFTAG_IMAGEDESCRIPTION`` - :samp:`DEM + GEOID height info projected on S2 tile` diff --git a/s1tiling/libs/otbwrappers/lia.py b/s1tiling/libs/otbwrappers/lia.py index 554237fb..e6893d2e 100644 --- a/s1tiling/libs/otbwrappers/lia.py +++ b/s1tiling/libs/otbwrappers/lia.py @@ -309,9 +309,10 @@ class ProjectGeoidToS2Tile(OTBStepFactory): super().update_image_metadata(meta, all_inputs) assert 'image_metadata' in meta imd = meta['image_metadata'] - imd['S2_TILE_CORRESPONDING_CODE'] = meta['tile_name'] - imd['SPATIAL_RESOLUTION'] = str(self.__out_spatial_res) - imd['ORTHORECTIFIED'] = 'true' + imd['GEOID_ORTHORECTIFICATION_INTERPOLATOR'] = self.__interpolation_method + imd['ORTHORECTIFIED'] = 'true' + imd['S2_TILE_CORRESPONDING_CODE'] = meta['tile_name'] + imd['SPATIAL_RESOLUTION'] = str(self.__out_spatial_res) def parameters(self, meta: Meta) -> OTBParameters: """ @@ -342,7 +343,6 @@ class SumAllHeights(OTBStepFactory): - `nodata.DEM` -- optional It requires the following information from the metadata dictionary: - """ def __init__(self, cfg: Configuration) -> None: """ @@ -409,6 +409,19 @@ class SumAllHeights(OTBStepFactory): assert 'in_s2_geoid' in keys return [input['in_s2_geoid'] for input in inputs if 'in_s2_geoid' in input.keys()][0] + def update_image_metadata(self, meta: Meta, all_inputs: InputList) -> None: + """ + Metadata coming from the DEM image are lost => we fetch them in the DEM file. + """ + super().update_image_metadata(meta, all_inputs) + + in_s2_dem = fetch_input_data('in_s2_dem', all_inputs).out_filename + dst = gdal.Open(in_s2_dem, gdal.GA_ReadOnly) + assert 'image_metadata' in meta + imd = meta['image_metadata'] + imd['DEM_RESAMPLING_METHOD'] = dst.GetMetadataItem('DEM_RESAMPLING_METHOD') + del dst + def parameters(self, meta: Meta) -> OTBParameters: """ Returns the parameters to use with :external:doc:`BandMath OTB application diff --git a/s1tiling/libs/otbwrappers/s1_to_s2.py b/s1tiling/libs/otbwrappers/s1_to_s2.py index 4754fb4d..45b24cb3 100644 --- a/s1tiling/libs/otbwrappers/s1_to_s2.py +++ b/s1tiling/libs/otbwrappers/s1_to_s2.py @@ -656,9 +656,10 @@ class _OrthoRectifierFactory(OTBStepFactory): super().update_image_metadata(meta, all_inputs) assert 'image_metadata' in meta imd = meta['image_metadata'] - imd['S2_TILE_CORRESPONDING_CODE'] = meta['tile_name'] - imd['ORTHORECTIFIED'] = 'true' - imd['SPATIAL_RESOLUTION'] = str(self.__out_spatial_res) + imd['ORTHORECTIFICATION_INTERPOLATOR'] = self.__interpolation_method + imd['ORTHORECTIFIED'] = 'true' + imd['S2_TILE_CORRESPONDING_CODE'] = meta['tile_name'] + imd['SPATIAL_RESOLUTION'] = str(self.__out_spatial_res) # S1 -> S2 => remove all SAR specific metadata inserted by OTB meta_to_remove_in_s2 = ( 'SARCalib*', 'SAR', 'PRF', 'RadarFrequency', 'RedDisplayChannel', -- GitLab From 9282b7a579ade9860c11b4e66423e2c97ea58beb Mon Sep 17 00:00:00 2001 From: Luc Hermitte <luc.hermitte@cs-soprasteria.com> Date: Mon, 3 Mar 2025 11:58:23 +0100 Subject: [PATCH 4/5] ENH: Fill DEM_INFO metadata from [Paths].dem_info --- docs/configuration.rst | 6 ++++ docs/files.rst | 49 ++++++++++++++++++++++----- docs/release_notes.rst | 7 ++-- s1tiling/libs/configuration.py | 4 ++- s1tiling/libs/otbwrappers/lia.py | 10 ++++++ s1tiling/libs/otbwrappers/s1_to_s2.py | 2 ++ s1tiling/resources/S1Processor.cfg | 4 +++ 7 files changed, 71 insertions(+), 11 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 9303db9c..3c114188 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -92,6 +92,12 @@ You can use this :download:`this template the keys from `eotile <https://github.com/CS-SI/eotile>`_ DEM database like for instance :file:`{{Product10}}.tif` for Copernicus 30m DEM. + .. _paths.dem_info: + * - ``dem_info`` + - DEM identifier to inject in the products GeoTIFF metadata under + ``DEM_INFO`` key. If not defined, the last part (basename) of + :ref:`[Paths].dem_dir <paths.dem_dir>` will be used. + .. _paths.srtm: * - ``srtm`` - **(deprecated)** Use :ref:`[PATHS].dem_dir <paths.dem_dir>`. Path to SRTM files. diff --git a/docs/files.rst b/docs/files.rst index 44c08b67..6331afb6 100644 --- a/docs/files.rst +++ b/docs/files.rst @@ -1,3 +1,8 @@ +.. # define a hard line break for HTML +.. |br| raw:: html + + <br /> + .. _files: .. index:: files @@ -72,6 +77,14 @@ Orthorectified S2 tiles - time of the second S1 image (in UTC format since v1.1) * - ``CALIBRATION`` - :ref:`chosen calibration option <Processing.calibration>` + * - ``DEM_INFO`` + - Key to identify which DEM has been used. |br| + Comes from :ref:`[Paths].dem_info <paths.dem_info>` or + :ref:`basename([Paths].dem_dir) <paths.dem_info>`. + * - ``DEM_INFO`` + - Key to identify which DEM has been used. |br| + Comes from :ref:`[Paths].dem_info <paths.dem_info>` or + :ref:`basename([Paths].dem_dir) <paths.dem_info>`. * - ``FLYING_UNIT_CODE`` - :samp:`s1{{a|b}}` * - ``IMAGE_TYPE`` @@ -259,8 +272,12 @@ Local Incidence Angle map files * - ``DATA_TYPE`` - :samp:`100 * degrees(LIA)` / :samp:`sin(LIA)` + * - ``DEM_INFO`` + - Key to identify which DEM has been used. |br| + Comes from :ref:`[Paths].dem_info <paths.dem_info>` or + :ref:`basename([Paths].dem_dir) <paths.dem_info>`. * - ``DEM_LIST`` - - List of DEM (SRTM currently) tiles used to generate the file + - List of DEM tiles used to generate the file * - ``DEM_RESAMPLING_METHOD`` - :ref:`chosen DEM resampling method option <Processing.dem_warp_resampling_method>` * - ``EOF_FILE`` @@ -528,16 +545,20 @@ DEM data projected on S2 tile * - Metadata - Value + * - ``DEM_INFO`` + - Key to identify which DEM has been used. |br| + Comes from :ref:`[Paths].dem_info <paths.dem_info>` or + :ref:`basename([Paths].dem_dir) <paths.dem_info>`. + * - ``DEM_LIST`` + - List of DEM tiles used to generate the file * - ``DEM_RESAMPLING_METHOD`` - :ref:`chosen DEM resampling method option <Processing.dem_warp_resampling_method>` + * - ``ORTHORECTIFIED`` + - :samp:`true` * - ``S2_TILE_CORRESPONDING_CODE`` - :samp:`{{tilename}}` * - ``SPATIAL_RESOLUTION`` - :ref:`chosen output spatial resolution option <Processing.output_spatial_resolution>` - * - ``DEM_LIST`` - - List of DEM (SRTM currently) tiles used to generate the file - * - ``ORTHORECTIFIED`` - - :samp:`true` * - ``TIFFTAG_IMAGEDESCRIPTION`` - :samp:`Warped DEM to S2 tile` @@ -607,8 +628,12 @@ files. * - Metadata - Value + * - ``DEM_INFO`` + - Key to identify which DEM has been used. |br| + Comes from :ref:`[Paths].dem_info <paths.dem_info>` or + :ref:`basename([Paths].dem_dir) <paths.dem_info>`. * - ``DEM_LIST`` - - List of DEM (SRTM currently) tiles used to generate the file + - List of DEM tiles used to generate the file * - ``EOF_FILE`` - Precise orbit file used to generate the file * - ``FLYING_UNIT_CODE`` @@ -721,8 +746,12 @@ Files of S1 coordinates projected on DEM geometry (deprecated) * - ``ACQUISITION_DATETIME`` - time of the first S1 image (in UTC format since v1.1) + * - ``DEM_INFO`` + - Key to identify which DEM has been used. |br| + Comes from :ref:`[Paths].dem_info <paths.dem_info>` or + :ref:`basename([Paths].dem_dir) <paths.dem_info>`. * - ``DEM_LIST`` - - List of DEM (SRTM currently) tiles used to generate the file + - List of DEM tiles used to generate the file * - ``FLYING_UNIT_CODE`` - :samp:`s1{{a|b}}` * - ``IMAGE_TYPE`` @@ -934,8 +963,12 @@ present in Sentinel-1 SAR input products. * - ``ACQUISITION_DATETIME`` - time of the first S1 image (in UTC format since v1.1) + * - ``DEM_INFO`` + - Key to identify which DEM has been used. |br| + Comes from :ref:`[Paths].dem_info <paths.dem_info>` or + :ref:`basename([Paths].dem_dir) <paths.dem_info>`. * - ``DEM_LIST`` - - List of DEM (SRTM currently) tiles used to generate the file + - List of DEM tiles used to generate the file * - ``FLYING_UNIT_CODE`` - :samp:`s1{{a|b}}` * - ``IMAGE_TYPE`` diff --git a/docs/release_notes.rst b/docs/release_notes.rst index fe035259..eb36f643 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -33,10 +33,13 @@ v1.2.0 Improvements directory structure is still supported for backward compatibility reasons. - Generate :ref:`maps of incidence angles to the WGS84 ellipsoid <ia-files>` (`#161 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/161>`_). -- Add :ref:`[Metadata] <metadata>` configuration section to specify extra - GeoTIFF metadata that will be set in the images produced by S1Tiling +- New GeoTIFF metadata are written in the images produced by S1Tiling (`#171 <https://gitlab.orfeo-toolbox.org/s1-tiling/s1tiling/-/issues/171>`_). + - :ref:`DEM_INFO <paths.dem_info>` that will be set when relevant, + - and any pairs of ``key=value`` that are specified in the :ref:`[Metadata] + <metadata>` configuration section. + Version 1.1.0 ------------- diff --git a/s1tiling/libs/configuration.py b/s1tiling/libs/configuration.py index 849a2c17..bfb2178f 100644 --- a/s1tiling/libs/configuration.py +++ b/s1tiling/libs/configuration.py @@ -319,7 +319,9 @@ class Configuration: # pylint: disable=too-many-instance-attributes # "dem_dir" or Fallback to old deprecated key: "srtm" #: Where DEM files are expected to be found: See :ref:`[PATHS.dem_dir] <paths.dem_dir>`! - self.dem = accessor.get('Paths', 'dem_dir', fallback='') or accessor.get('Paths', 'srtm') + self.dem = accessor.get('Paths', 'dem_dir', fallback='') or accessor.get('Paths', 'srtm') + #: DEM identifier to save in GeoTIFF metadata: See :ref:`[PATHS.dem_info] <paths.dem_info>`! + self.dem_info = accessor.get('Paths', 'dem_info', fallback=os.path.basename(self.dem)) dem_database = accessor.get('Paths', 'dem_database', fallback='') # TODO: Inject resource_dir/'shapefile' if relative dir and not existing #: Path to the internal DEM tiles database: automatically set diff --git a/s1tiling/libs/otbwrappers/lia.py b/s1tiling/libs/otbwrappers/lia.py index e6893d2e..e5f8e618 100644 --- a/s1tiling/libs/otbwrappers/lia.py +++ b/s1tiling/libs/otbwrappers/lia.py @@ -201,6 +201,7 @@ class ProjectDEMToS2Tile(ExecutableStepFactory): gen_output_filename=TemplateOutputFilenameGenerator(fname_fmt), image_description="Warped DEM to S2 tile", ) + self.__dem_info = cfg.dem_info self.__out_spatial_res = cfg.out_spatial_res self.__resampling_method = cfg.dem_warp_resampling_method self.__nb_threads = cfg.nb_procs @@ -226,6 +227,7 @@ class ProjectDEMToS2Tile(ExecutableStepFactory): imd = meta['image_metadata'] imd['S2_TILE_CORRESPONDING_CODE'] = meta['tile_name'] imd['SPATIAL_RESOLUTION'] = str(self.__out_spatial_res) + imd['DEM_INFO'] = self.__dem_info imd['DEM_RESAMPLING_METHOD'] = self.__resampling_method imd['ORTHORECTIFIED'] = 'true' # TODO: Import DEM_LIST from input VRT image @@ -486,6 +488,7 @@ class ComputeGroundAndSatPositionsOnDEMFromEOF(OTBStepFactory): ) self.__cfg = cfg # Will be used to access cached DEM intersecting S2 tile self.__nodata = nodata_XYZ(cfg) + self.__dem_info = cfg.dem_info def _update_filename_meta_post_hook(self, meta: Meta) -> None: """ @@ -557,6 +560,7 @@ class ComputeGroundAndSatPositionsOnDEMFromEOF(OTBStepFactory): assert 'image_metadata' in meta imd = meta['image_metadata'] imd['POLARIZATION'] = "" # Clear polarization information (makes no sense here) + imd['DEM_INFO'] = self.__dem_info imd['DEM_LIST'] = ', '.join(meta['dems']) imd['band.DirectionToScanDEM*'] = '' imd['band.Gain'] = '' @@ -649,6 +653,7 @@ class ComputeGroundAndSatPositionsOnDEM(OTBStepFactory): ) self.__cfg = cfg # Will be used to access cached DEM intersecting S2 tile self.__nodata = nodata_XYZ(cfg) + self.__dem_info = cfg.dem_info @staticmethod def reduce_inputs(inputs: List[Meta]) -> List: @@ -771,6 +776,7 @@ class ComputeGroundAndSatPositionsOnDEM(OTBStepFactory): assert 'image_metadata' in meta imd = meta['image_metadata'] imd['POLARIZATION'] = "" # Clear polarization information (makes no sense here) + imd['DEM_INFO'] = self.__dem_info imd['DEM_LIST'] = ', '.join(meta['dems']) imd['band.DirectionToScanDEM*'] = '' imd['band.Gain'] = '' @@ -1445,6 +1451,7 @@ class SARDEMProjection(OTBStepFactory): self.__dem_db_filepath = cfg.dem_db_filepath self.__dem_field_ids = cfg.dem_field_ids self.__dem_main_field_id = cfg.dem_main_field_id + self.__dem_info = cfg.dem_info def _update_filename_meta_pre_hook(self, meta: Meta) -> Meta: """ @@ -1492,6 +1499,7 @@ class SARDEMProjection(OTBStepFactory): assert 'image_metadata' in meta imd = meta['image_metadata'] imd['POLARIZATION'] = "" # Clear polarization information (makes no sense here) + imd['DEM_INFO'] = self.__dem_info imd['DEM_LIST'] = ', '.join(meta['dems']) def add_image_metadata(self, meta: Meta, app) -> None: @@ -1869,6 +1877,7 @@ class ConcatenateLIA(_ConcatenatorFactory): 'LIA' : extended_filename_lia_degree(cfg), 'sin_LIA' : extended_filename_lia_sin(cfg), } + self.__dem_info = cfg.dem_info def _update_filename_meta_post_hook(self, meta: Meta) -> None: """ @@ -1885,6 +1894,7 @@ class ConcatenateLIA(_ConcatenatorFactory): """ super().update_image_metadata(meta, all_inputs) imd = meta['image_metadata'] + imd['DEM_INFO'] = self.__dem_info imd['DEM_LIST'] = "" # Clear DEM_LIST information (a merge of 2 lists should be done actually) def complete_meta(self, meta: Meta, all_inputs: InputList) -> Meta: diff --git a/s1tiling/libs/otbwrappers/s1_to_s2.py b/s1tiling/libs/otbwrappers/s1_to_s2.py index 45b24cb3..6cabff3a 100644 --- a/s1tiling/libs/otbwrappers/s1_to_s2.py +++ b/s1tiling/libs/otbwrappers/s1_to_s2.py @@ -636,6 +636,7 @@ class _OrthoRectifierFactory(OTBStepFactory): self.__grid_spacing = cfg.grid_spacing self.__interpolation_method = cfg.interpolation_method self.__tmp_dem_dir = cfg.tmp_dem_dir + self.__dem_info = cfg.dem_info # self.__tmpdir = cfg.tmpdir # Some workaround when ortho is not sequenced along with calibration # (and locally override calibration type in case of normlim calibration) @@ -660,6 +661,7 @@ class _OrthoRectifierFactory(OTBStepFactory): imd['ORTHORECTIFIED'] = 'true' imd['S2_TILE_CORRESPONDING_CODE'] = meta['tile_name'] imd['SPATIAL_RESOLUTION'] = str(self.__out_spatial_res) + imd['DEM_INFO'] = self.__dem_info # S1 -> S2 => remove all SAR specific metadata inserted by OTB meta_to_remove_in_s2 = ( 'SARCalib*', 'SAR', 'PRF', 'RadarFrequency', 'RedDisplayChannel', diff --git a/s1tiling/resources/S1Processor.cfg b/s1tiling/resources/S1Processor.cfg index 3659ffec..e28d66b1 100644 --- a/s1tiling/resources/S1Processor.cfg +++ b/s1tiling/resources/S1Processor.cfg @@ -20,6 +20,10 @@ s1_images : /datalocal2/share2/Asia/raw # Path where Precise Orbit EOF files are downloaded eof_dir : /datalocal2/share2/Asia/eof +# Special information to write in GeoTIFF metadata +# Unlike information in [Metadata], this will be written only when DEM are used +dem_info : STRM 30m + # Path to SRTM/DEM files dem_dir : /datalocal/share/SRTM -- GitLab From 174b2d65c6984b5586bb3b9acf605a0bde1b86da Mon Sep 17 00:00:00 2001 From: Luc Hermitte <luc.hermitte@cs-soprasteria.com> Date: Mon, 3 Mar 2025 14:46:03 +0100 Subject: [PATCH 5/5] TST: Update tests for new metadata --- s1tiling/libs/otbwrappers/lia.py | 15 +- tests/mock_helpers.py | 1 + .../test_build_dependencies_and_tasks.py | 2 + tests/test_0200306-NR.py | 301 ++++++++++-------- 4 files changed, 177 insertions(+), 142 deletions(-) diff --git a/s1tiling/libs/otbwrappers/lia.py b/s1tiling/libs/otbwrappers/lia.py index e5f8e618..af980257 100644 --- a/s1tiling/libs/otbwrappers/lia.py +++ b/s1tiling/libs/otbwrappers/lia.py @@ -411,6 +411,17 @@ class SumAllHeights(OTBStepFactory): assert 'in_s2_geoid' in keys return [input['in_s2_geoid'] for input in inputs if 'in_s2_geoid' in input.keys()][0] + def fetch_upstream_dem_resampling_method(self, inputpath: str, meta: Meta): + logger.debug("Fetch DEM_RESAMPLING_METHOD from '%s'", inputpath) + if not is_running_dry(meta): # FIXME: this info is no longer in meta! + dst = gdal.Open(inputpath, gdal.GA_ReadOnly) + if not dst: + raise RuntimeError(f"Cannot open DEM/S2 file '{inputpath}' to collect DEM_RESAMPLING_METHOD metadata.") + res = dst.GetMetadataItem('DEM_RESAMPLING_METHOD') + del dst + return res + return 'No idea in dry run mode' + def update_image_metadata(self, meta: Meta, all_inputs: InputList) -> None: """ Metadata coming from the DEM image are lost => we fetch them in the DEM file. @@ -418,11 +429,9 @@ class SumAllHeights(OTBStepFactory): super().update_image_metadata(meta, all_inputs) in_s2_dem = fetch_input_data('in_s2_dem', all_inputs).out_filename - dst = gdal.Open(in_s2_dem, gdal.GA_ReadOnly) assert 'image_metadata' in meta imd = meta['image_metadata'] - imd['DEM_RESAMPLING_METHOD'] = dst.GetMetadataItem('DEM_RESAMPLING_METHOD') - del dst + imd['DEM_RESAMPLING_METHOD'] = self.fetch_upstream_dem_resampling_method(in_s2_dem, meta) def parameters(self, meta: Meta) -> OTBParameters: """ diff --git a/tests/mock_helpers.py b/tests/mock_helpers.py index 340bccdc..66fe2031 100644 --- a/tests/mock_helpers.py +++ b/tests/mock_helpers.py @@ -138,6 +138,7 @@ def declare_know_files( meta['gain'] = 42 return meta mocker.patch('s1tiling.libs.otbwrappers.SARCartesianMeanEstimation.fetch_direction', lambda slf, ip, mt : mock_direction_to_scan(slf, mt)) + mocker.patch('s1tiling.libs.otbwrappers.SumAllHeights.fetch_upstream_dem_resampling_method', lambda slf, ip, mt : 'cubic') def mock_fetch_nodata_value(inputpath, is_running_dry, default_value, band_nr:int = 1) -> float: return default_value diff --git a/tests/step_defs/test_build_dependencies_and_tasks.py b/tests/step_defs/test_build_dependencies_and_tasks.py index 9dfc7e85..f23c17e9 100644 --- a/tests/step_defs/test_build_dependencies_and_tasks.py +++ b/tests/step_defs/test_build_dependencies_and_tasks.py @@ -270,6 +270,8 @@ class Configuration(): } self.dname_fmt = {} self.creation_options = {} + self.extra_metadata = {} + self.dem_info = '' def isfile(filename, existing_files) -> bool: diff --git a/tests/test_0200306-NR.py b/tests/test_0200306-NR.py index 025b35c7..a2b38d01 100644 --- a/tests/test_0200306-NR.py +++ b/tests/test_0200306-NR.py @@ -141,25 +141,27 @@ def test_33NWB_202001_NR_execute_OTB(baselinedir, outputdir, liadir, tmpdir, dem ("Comparison of %s against %s failed" % (produced, expected)) # expected_md = comparable_metadata(expected) expected_md = { - 'ACQUISITION_DATETIME' : '2020:01:08T04:41:50Z', - 'ACQUISITION_DATETIME_1' : '2020:01:08T04:41:50Z', - 'ACQUISITION_DATETIME_2' : '2020:01:08T04:42:15Z', - 'AREA_OR_POINT' : 'Area', - 'CALIBRATION' : 'sigma', - 'FLYING_UNIT_CODE' : 's1a', - 'IMAGE_TYPE' : image_type, - 'INPUT_S1_IMAGES' : 'S1A_IW_GRDH_1SDV_20200108T044150_20200108T044215_030704_038506_C7F5, S1A_IW_GRDH_1SDV_20200108T044215_20200108T044240_030704_038506_D953', - 'NOISE_REMOVED' : 'False', - 'ORBIT_DIRECTION' : 'DES', - 'ORBIT_NUMBER' : '030704', - 'ORTHORECTIFIED' : 'true', - 'POLARIZATION' : polar, - 'RELATIVE_ORBIT_NUMBER' : '007', - 'S2_TILE_CORRESPONDING_CODE' : '33NWB', - 'SPATIAL_RESOLUTION' : '10.0', - 'TIFFTAG_IMAGEDESCRIPTION' : descr, - 'TIFFTAG_SOFTWARE' : 'S1 Tiling', - } + 'ACQUISITION_DATETIME' : '2020:01:08T04:41:50Z', + 'ACQUISITION_DATETIME_1' : '2020:01:08T04:41:50Z', + 'ACQUISITION_DATETIME_2' : '2020:01:08T04:42:15Z', + 'AREA_OR_POINT' : 'Area', + 'CALIBRATION' : 'sigma', + 'DEM_INFO' : 'SRTM_30_hgt', + 'FLYING_UNIT_CODE' : 's1a', + 'IMAGE_TYPE' : image_type, + 'INPUT_S1_IMAGES' : 'S1A_IW_GRDH_1SDV_20200108T044150_20200108T044215_030704_038506_C7F5, S1A_IW_GRDH_1SDV_20200108T044215_20200108T044240_030704_038506_D953', + 'NOISE_REMOVED' : 'False', + 'ORBIT_DIRECTION' : 'DES', + 'ORBIT_NUMBER' : '030704', + 'ORTHORECTIFICATION_INTERPOLATOR': 'nn', + 'ORTHORECTIFIED' : 'true', + 'POLARIZATION' : polar, + 'RELATIVE_ORBIT_NUMBER' : '007', + 'S2_TILE_CORRESPONDING_CODE' : '33NWB', + 'SPATIAL_RESOLUTION' : '10.0', + 'TIFFTAG_IMAGEDESCRIPTION' : descr, + 'TIFFTAG_SOFTWARE' : 'S1 Tiling', + } assert expected_md == comparable_metadata(produced) # The following line permits to test otb_compare correctly detect differences when # called from pytest. @@ -223,27 +225,29 @@ def test_33NWB_202001_NR_masks_only_execute_OTB(baselinedir, outputdir, liadir, ("Comparison of %s against %s failed" % (produced, expected)) # expected_md = comparable_metadata(expected) expected_md = { - 'ACQUISITION_DATETIME' : '2020:01:08T04:41:50Z', # Start point has it - # For now, the start points don't have this... - 'ACQUISITION_DATETIME_1' : '2020:01:08T04:41:50Z', - 'ACQUISITION_DATETIME_2' : '2020:01:08T04:42:15Z', - 'AREA_OR_POINT' : 'Area', - 'CALIBRATION' : 'sigma', - 'FLYING_UNIT_CODE' : 's1a', - 'IMAGE_TYPE' : 'MASK', - 'INPUT_S1_IMAGES' : 'S1A_IW_GRDH_1SDV_20200108T044150_20200108T044215_030704_038506_C7F5, S1A_IW_GRDH_1SDV_20200108T044215_20200108T044240_030704_038506_D953', - # For now, the start points don't have this... - 'NOISE_REMOVED' : 'False', - 'ORBIT_DIRECTION' : 'DES', - 'ORBIT_NUMBER' : '030704', - 'ORTHORECTIFIED' : 'true', - 'POLARIZATION' : polar, - 'RELATIVE_ORBIT_NUMBER' : '007', - 'S2_TILE_CORRESPONDING_CODE' : '33NWB', - 'SPATIAL_RESOLUTION' : '10.0', - 'TIFFTAG_IMAGEDESCRIPTION' : 'Orthorectified Sentinel-1A IW GRD smoothed border mask S2 tile', - 'TIFFTAG_SOFTWARE' : 'S1 Tiling', - } + 'ACQUISITION_DATETIME' : '2020:01:08T04:41:50Z', # Start point has it + # For now, the start points don't have this... + 'ACQUISITION_DATETIME_1' : '2020:01:08T04:41:50Z', + 'ACQUISITION_DATETIME_2' : '2020:01:08T04:42:15Z', + 'AREA_OR_POINT' : 'Area', + 'CALIBRATION' : 'sigma', + 'DEM_INFO' : 'SRTM_30_hgt', + 'FLYING_UNIT_CODE' : 's1a', + 'IMAGE_TYPE' : 'MASK', + 'INPUT_S1_IMAGES' : 'S1A_IW_GRDH_1SDV_20200108T044150_20200108T044215_030704_038506_C7F5, S1A_IW_GRDH_1SDV_20200108T044215_20200108T044240_030704_038506_D953', + # For now, the start points don't have this... + 'NOISE_REMOVED' : 'False', + 'ORBIT_DIRECTION' : 'DES', + 'ORBIT_NUMBER' : '030704', + 'ORTHORECTIFICATION_INTERPOLATOR': 'nn', + 'ORTHORECTIFIED' : 'true', + 'POLARIZATION' : polar, + 'RELATIVE_ORBIT_NUMBER' : '007', + 'S2_TILE_CORRESPONDING_CODE' : '33NWB', + 'SPATIAL_RESOLUTION' : '10.0', + 'TIFFTAG_IMAGEDESCRIPTION' : 'Orthorectified Sentinel-1A IW GRD smoothed border mask S2 tile', + 'TIFFTAG_SOFTWARE' : 'S1 Tiling', + } assert expected_md == comparable_metadata(produced) @@ -362,35 +366,37 @@ def mock_upto_concat_S2( 'io.out' : orthofile, }, None, { - 'ORTHORECTIFIED' : 'true', - 'S2_TILE_CORRESPONDING_CODE': '33NWB', - 'SPATIAL_RESOLUTION' : '10.0', - 'TIFFTAG_IMAGEDESCRIPTION' : f'{raw_calibration} calibrated orthorectified Sentinel-1A IW GRD', - 'AbsoluteCalibrationConstant' : '', - 'AcquisitionDate' : '', - 'AcquisitionStartTime' : '', - 'AcquisitionStopTime' : '', - 'AverageSceneHeight' : '', - 'BeamMode' : '', - 'BeamSwath' : '', - 'BlueDisplayChannel' : '', - 'GreenDisplayChannel' : '', - 'Instrument' : '', - 'LineSpacing' : '', - 'Mission' : '', - 'Mode' : '', - 'NumberOfColumns' : '', - 'NumberOfLines' : '', - 'OrbitDirection' : '', - 'OrbitNumber' : '', - 'PRF' : '', - 'PixelSpacing' : '', - 'RadarFrequency' : '', - 'RedDisplayChannel' : '', - 'SAR' : '', - 'SARCalib*' : '', - 'SensorID' : '', - 'Swath' : '', + 'DEM_INFO' : 'SRTM_30_hgt', + 'ORTHORECTIFICATION_INTERPOLATOR': 'nn', + 'ORTHORECTIFIED' : 'true', + 'S2_TILE_CORRESPONDING_CODE' : '33NWB', + 'SPATIAL_RESOLUTION' : '10.0', + 'TIFFTAG_IMAGEDESCRIPTION' : f'{raw_calibration} calibrated orthorectified Sentinel-1A IW GRD', + 'AbsoluteCalibrationConstant' : '', + 'AcquisitionDate' : '', + 'AcquisitionStartTime' : '', + 'AcquisitionStopTime' : '', + 'AverageSceneHeight' : '', + 'BeamMode' : '', + 'BeamSwath' : '', + 'BlueDisplayChannel' : '', + 'GreenDisplayChannel' : '', + 'Instrument' : '', + 'LineSpacing' : '', + 'Mission' : '', + 'Mode' : '', + 'NumberOfColumns' : '', + 'NumberOfLines' : '', + 'OrbitDirection' : '', + 'OrbitNumber' : '', + 'PRF' : '', + 'PixelSpacing' : '', + 'RadarFrequency' : '', + 'RedDisplayChannel' : '', + 'SAR' : '', + 'SARCalib*' : '', + 'SensorID' : '', + 'Swath' : '', }) if N == 1: @@ -490,6 +496,7 @@ def mock_LIA_v1_0(application_mocker: OTBApplicationsMockContext, file_db: FileD }, None, { 'ACQUISITION_DATETIME' : file_db.start_time(idx), + 'DEM_INFO' : 'SRTM_30_hgt', 'DEM_LIST' : ', '.join(exp_dem_names), 'FLYING_UNIT_CODE' : 's1a', 'IMAGE_TYPE' : 'GRD', @@ -564,36 +571,38 @@ def mock_LIA_v1_0(application_mocker: OTBApplicationsMockContext, file_db: FileD 'io.out' : file_db.orthodegLIAfile(idx, True), }, {'io.out': otb.ImagePixelType_int16}, { - 'DATA_TYPE' : '100 * degrees(LIA)', - 'ORTHORECTIFIED' : 'true', - 'S2_TILE_CORRESPONDING_CODE': '33NWB', - 'SPATIAL_RESOLUTION' : '10.0', - 'TIFFTAG_IMAGEDESCRIPTION' : 'Orthorectified LIA Sentinel-1A IW GRD', - 'AbsoluteCalibrationConstant' : '', - 'AcquisitionDate' : '', - 'AcquisitionStartTime' : '', - 'AcquisitionStopTime' : '', - 'AverageSceneHeight' : '', - 'BeamMode' : '', - 'BeamSwath' : '', - 'BlueDisplayChannel' : '', - 'GreenDisplayChannel' : '', - 'Instrument' : '', - 'LineSpacing' : '', - 'Mission' : '', - 'Mode' : '', - 'NumberOfColumns' : '', - 'NumberOfLines' : '', - 'OrbitDirection' : '', - 'OrbitNumber' : '', - 'PRF' : '', - 'PixelSpacing' : '', - 'RadarFrequency' : '', - 'RedDisplayChannel' : '', - 'SAR' : '', - 'SARCalib*' : '', - 'SensorID' : '', - 'Swath' : '', + 'DATA_TYPE' : '100 * degrees(LIA)', + 'DEM_INFO' : 'SRTM_30_hgt', + 'ORTHORECTIFICATION_INTERPOLATOR': 'nn', + 'ORTHORECTIFIED' : 'true', + 'S2_TILE_CORRESPONDING_CODE' : '33NWB', + 'SPATIAL_RESOLUTION' : '10.0', + 'TIFFTAG_IMAGEDESCRIPTION' : 'Orthorectified LIA Sentinel-1A IW GRD', + 'AbsoluteCalibrationConstant' : '', + 'AcquisitionDate' : '', + 'AcquisitionStartTime' : '', + 'AcquisitionStopTime' : '', + 'AverageSceneHeight' : '', + 'BeamMode' : '', + 'BeamSwath' : '', + 'BlueDisplayChannel' : '', + 'GreenDisplayChannel' : '', + 'Instrument' : '', + 'LineSpacing' : '', + 'Mission' : '', + 'Mode' : '', + 'NumberOfColumns' : '', + 'NumberOfLines' : '', + 'OrbitDirection' : '', + 'OrbitNumber' : '', + 'PRF' : '', + 'PixelSpacing' : '', + 'RadarFrequency' : '', + 'RedDisplayChannel' : '', + 'SAR' : '', + 'SARCalib*' : '', + 'SensorID' : '', + 'Swath' : '', }) application_mocker.set_expectations('OrthoRectification', { @@ -615,36 +624,38 @@ def mock_LIA_v1_0(application_mocker: OTBApplicationsMockContext, file_db: FileD 'io.out' : file_db.orthosinLIAfile(idx, True), }, None, { - 'DATA_TYPE' : 'sin(LIA)', - 'ORTHORECTIFIED' : 'true', - 'S2_TILE_CORRESPONDING_CODE': '33NWB', - 'SPATIAL_RESOLUTION' : '10.0', - 'TIFFTAG_IMAGEDESCRIPTION' : 'Orthorectified sin_LIA Sentinel-1A IW GRD', - 'AbsoluteCalibrationConstant' : '', - 'AcquisitionDate' : '', - 'AcquisitionStartTime' : '', - 'AcquisitionStopTime' : '', - 'AverageSceneHeight' : '', - 'BeamMode' : '', - 'BeamSwath' : '', - 'BlueDisplayChannel' : '', - 'GreenDisplayChannel' : '', - 'Instrument' : '', - 'LineSpacing' : '', - 'Mission' : '', - 'Mode' : '', - 'NumberOfColumns' : '', - 'NumberOfLines' : '', - 'OrbitDirection' : '', - 'OrbitNumber' : '', - 'PRF' : '', - 'PixelSpacing' : '', - 'RadarFrequency' : '', - 'RedDisplayChannel' : '', - 'SAR' : '', - 'SARCalib*' : '', - 'SensorID' : '', - 'Swath' : '', + 'DATA_TYPE' : 'sin(LIA)', + 'DEM_INFO' : 'SRTM_30_hgt', + 'ORTHORECTIFICATION_INTERPOLATOR': 'nn', + 'ORTHORECTIFIED' : 'true', + 'S2_TILE_CORRESPONDING_CODE' : '33NWB', + 'SPATIAL_RESOLUTION' : '10.0', + 'TIFFTAG_IMAGEDESCRIPTION' : 'Orthorectified sin_LIA Sentinel-1A IW GRD', + 'AbsoluteCalibrationConstant' : '', + 'AcquisitionDate' : '', + 'AcquisitionStartTime' : '', + 'AcquisitionStopTime' : '', + 'AverageSceneHeight' : '', + 'BeamMode' : '', + 'BeamSwath' : '', + 'BlueDisplayChannel' : '', + 'GreenDisplayChannel' : '', + 'Instrument' : '', + 'LineSpacing' : '', + 'Mission' : '', + 'Mode' : '', + 'NumberOfColumns' : '', + 'NumberOfLines' : '', + 'OrbitDirection' : '', + 'OrbitNumber' : '', + 'PRF' : '', + 'PixelSpacing' : '', + 'RadarFrequency' : '', + 'RedDisplayChannel' : '', + 'SAR' : '', + 'SARCalib*' : '', + 'SensorID' : '', + 'Swath' : '', }) # endfor on 2 consecutive images @@ -658,6 +669,7 @@ def mock_LIA_v1_0(application_mocker: OTBApplicationsMockContext, file_db: FileD 'ACQUISITION_DATETIME' : file_db.start_time_for_two(0), 'ACQUISITION_DATETIME_1' : file_db.start_time(0), 'ACQUISITION_DATETIME_2' : file_db.start_time(1), + 'DEM_INFO' : 'SRTM_30_hgt', 'DEM_LIST' : '', # <=> Removing the key 'INPUT_S1_IMAGES' : '%s, %s' % (file_db.product_name(0), file_db.product_name(1)), 'TIFFTAG_IMAGEDESCRIPTION' : 'Orthorectified LIA Sentinel-1A IW GRD', @@ -672,6 +684,7 @@ def mock_LIA_v1_0(application_mocker: OTBApplicationsMockContext, file_db: FileD 'ACQUISITION_DATETIME' : file_db.start_time_for_two(0), 'ACQUISITION_DATETIME_1' : file_db.start_time(0), 'ACQUISITION_DATETIME_2' : file_db.start_time(1), + 'DEM_INFO' : 'SRTM_30_hgt', 'DEM_LIST' : '', # <=> Removing the key 'INPUT_S1_IMAGES' : '%s, %s' % (file_db.product_name(0), file_db.product_name(1)), 'TIFFTAG_IMAGEDESCRIPTION' : 'Orthorectified sin_LIA Sentinel-1A IW GRD', @@ -713,6 +726,7 @@ def mock_LIA_v1_1(application_mocker: OTBApplicationsMockContext, file_db: FileD ], None, { 'S2_TILE_CORRESPONDING_CODE' : '33NWB', 'SPATIAL_RESOLUTION' : f"{spacing}", + 'DEM_INFO' : 'SRTM_30_hgt', 'DEM_RESAMPLING_METHOD' : 'cubic', 'TIFFTAG_IMAGEDESCRIPTION' : 'Warped DEM to S2 tile', 'ORTHORECTIFIED' : 'true', @@ -731,9 +745,11 @@ def mock_LIA_v1_1(application_mocker: OTBApplicationsMockContext, file_db: FileD }, None, { # 'ACQUISITION_DATETIME' : file_db.start_time(0), # 'DEM_LIST' : ', '.join(exp_dem_names), - 'S2_TILE_CORRESPONDING_CODE' : '33NWB', - 'SPATIAL_RESOLUTION' : f"{spacing}", - 'TIFFTAG_IMAGEDESCRIPTION' : 'Geoid superimposed on S2 tile', + 'S2_TILE_CORRESPONDING_CODE' : '33NWB', + 'SPATIAL_RESOLUTION' : f"{spacing}", + 'TIFFTAG_IMAGEDESCRIPTION' : 'Geoid superimposed on S2 tile', + 'GEOID_ORTHORECTIFICATION_INTERPOLATOR': 'nn', + 'ORTHORECTIFIED' : 'true', }) # Sum DEM + GEOID @@ -748,8 +764,9 @@ def mock_LIA_v1_1(application_mocker: OTBApplicationsMockContext, file_db: FileD 'exp' : f'{is_nodata_DEM_bandmath} ? {nodata_DEM} : im1b1+im2b1', 'out' : file_db.height_on_s2(True), }, None, { - 'TIFFTAG_IMAGEDESCRIPTION' : 'DEM + GEOID height info projected on S2 tile', - 'ORTHORECTIFIED' : 'true', + 'TIFFTAG_IMAGEDESCRIPTION' : 'DEM + GEOID height info projected on S2 tile', + 'DEM_RESAMPLING_METHOD' : 'cubic', + 'ORTHORECTIFIED' : 'true', }) # ComputeGroundAndSatPositionsOnDEM application_mocker.set_expectations('SARDEMProjection2', { @@ -764,6 +781,7 @@ def mock_LIA_v1_1(application_mocker: OTBApplicationsMockContext, file_db: FileD 'out' : file_db.xyz_on_s2(True), }, None, { # 'ACQUISITION_DATETIME' : file_db.start_time(0), + 'DEM_INFO' : 'SRTM_30_hgt', 'DEM_LIST' : ', '.join(exp_dem_names), 'IMAGE_TYPE' : 'XYZ', 'TIFFTAG_IMAGEDESCRIPTION' : 'XYZ ground and satellite positions on S2 tile', @@ -833,6 +851,7 @@ def mock_LIA_v1_2(application_mocker: OTBApplicationsMockContext, file_db: FileD ], None, { 'S2_TILE_CORRESPONDING_CODE' : '33NWB', 'SPATIAL_RESOLUTION' : f"{spacing}", + 'DEM_INFO' : 'SRTM_30_hgt', 'DEM_RESAMPLING_METHOD' : 'cubic', 'TIFFTAG_IMAGEDESCRIPTION' : 'Warped DEM to S2 tile', 'ORTHORECTIFIED' : 'true', @@ -851,9 +870,11 @@ def mock_LIA_v1_2(application_mocker: OTBApplicationsMockContext, file_db: FileD }, None, { # 'ACQUISITION_DATETIME' : file_db.start_time(0), # 'DEM_LIST' : ', '.join(exp_dem_names), - 'S2_TILE_CORRESPONDING_CODE' : '33NWB', - 'SPATIAL_RESOLUTION' : f"{spacing}", - 'TIFFTAG_IMAGEDESCRIPTION' : 'Geoid superimposed on S2 tile', + 'S2_TILE_CORRESPONDING_CODE' : '33NWB', + 'SPATIAL_RESOLUTION' : f"{spacing}", + 'TIFFTAG_IMAGEDESCRIPTION' : 'Geoid superimposed on S2 tile', + 'GEOID_ORTHORECTIFICATION_INTERPOLATOR': 'nn', + 'ORTHORECTIFIED' : 'true', }) # Sum DEM + GEOID @@ -868,8 +889,9 @@ def mock_LIA_v1_2(application_mocker: OTBApplicationsMockContext, file_db: FileD 'exp' : f'{is_nodata_DEM_bandmath} ? {nodata_DEM} : im1b1+im2b1', 'out' : file_db.height_on_s2(True), }, None, { - 'TIFFTAG_IMAGEDESCRIPTION' : 'DEM + GEOID height info projected on S2 tile', - 'ORTHORECTIFIED' : 'true', + 'TIFFTAG_IMAGEDESCRIPTION' : 'DEM + GEOID height info projected on S2 tile', + 'DEM_RESAMPLING_METHOD' : 'cubic', + 'ORTHORECTIFIED' : 'true', }) # ComputeGroundAndSatPositionsOnDEM application_mocker.set_expectations('SARComputeGroundAndSatPositionsOnDEM', { @@ -884,6 +906,7 @@ def mock_LIA_v1_2(application_mocker: OTBApplicationsMockContext, file_db: FileD 'out' : file_db.xyz_on_s2(True), }, None, { # 'ACQUISITION_DATETIME' : file_db.start_time(0), + 'DEM_INFO' : 'SRTM_30_hgt', 'DEM_LIST' : ', '.join(exp_dem_names), 'EOF_FILE' : os.path.basename(file_db.eof_for_s2()), 'FLYING_UNIT_CODE' : 's1a', -- GitLab