From 0202839713a44ba76a02dbfe32e0011ba6036262 Mon Sep 17 00:00:00 2001 From: BRAUN REMI Date: Tue, 13 Aug 2024 14:44:10 +0200 Subject: [PATCH] OPTIM: Use default (and optimized) predictor in `rasters.write` if SNAP is version 10 or higher #173 --- CHANGES.md | 3 +- eoreader/bands/__init__.py | 2 +- eoreader/products/sar/cosmo_product.py | 6 +- eoreader/products/sar/sar_product.py | 91 ++++++++++++++++++++------ 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 43f8e0b4..4a2a5f2a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,7 +4,8 @@ - ENH: Add a `BandType` alias for any types that could be a band: a string, a `BandNames` or any of its children: Spectral, SAR, DEM or Cloud band names - FIX: Get better window name (if available) when writing bands on disk (in tmp folder) -- COMPAT: EOReader handles with SNAP10 ([#165](https://github.com/sertit/eoreader/issues/165)) +- OPTIM: Use default (and optimized) predictor in `rasters.write` if SNAP is version 10 or higher ([#173](https://github.com/sertit/eoreader/issues/173)) +- COMPAT: EOReader works correctly with SNAP 10 ([#165](https://github.com/sertit/eoreader/issues/165)) ## 0.21.2 (2024-07-30) diff --git a/eoreader/bands/__init__.py b/eoreader/bands/__init__.py index 79e52dec..3327d4e7 100644 --- a/eoreader/bands/__init__.py +++ b/eoreader/bands/__init__.py @@ -319,7 +319,7 @@ def is_sar_band(band: BandType) -> bool: return is_valid -def is_sat_band(BandType) -> bool: +def is_sat_band(band: BandType) -> bool: """ Returns True if is a satellite band (from both :code:`SarBandNames` or :code:`SpectralBandNames`) diff --git a/eoreader/products/sar/cosmo_product.py b/eoreader/products/sar/cosmo_product.py index 487fd72d..12a9a7fe 100644 --- a/eoreader/products/sar/cosmo_product.py +++ b/eoreader/products/sar/cosmo_product.py @@ -43,7 +43,6 @@ from eoreader.exceptions import InvalidProductError from eoreader.products import SarProduct, SarProductType from eoreader.products.product import OrbitDirection -from eoreader.products.sar.sar_product import SAR_PREDICTOR LOGGER = logging.getLogger(EOREADER_NAME) @@ -577,15 +576,14 @@ def _pre_process_sar(self, band, pixel_size: float = None, **kwargs) -> str: # Write # WARNING: Set nodata to 0 here as it is the value wanted by SNAP ! - # SNAP fails with classic predictor !!! Set the predictor to the default value (1) !!! + # SNAP < 10.0.0 fails with classic predictor !!! Set the predictor to the default value (1) !!! # Caused by: javax.imageio.IIOException: Illegal value for Predictor in TIFF file - # https://forum.step.esa.int/t/exception-found-when-reading-compressed-tif/654/7 rasters_rio.write( merged_array, merged_meta, pp_path, nodata=self._snap_no_data, - predictor=SAR_PREDICTOR, + predictor=self._get_predictor(), ) return pp_path diff --git a/eoreader/products/sar/sar_product.py b/eoreader/products/sar/sar_product.py index 9d2a42e2..2465d6dd 100644 --- a/eoreader/products/sar/sar_product.py +++ b/eoreader/products/sar/sar_product.py @@ -53,15 +53,6 @@ LOGGER = logging.getLogger(EOREADER_NAME) -SAR_PREDICTOR = 1 -""" -Set LZW predictor to 1 in order SNAP to be able to read this GEoTiff. - -Caused by: javax.imageio.IIOException: Illegal value for Predictor in TIFF file -https://forum.step.esa.int/t/exception-found-when-reading-compressed-tif/654/7 - -""" - @unique class SnapDems(ListEnum): @@ -193,6 +184,8 @@ def __init__( self._snap_no_data = 0 self._raw_no_data = 0 + self._need_snap = None + # Calibrate or not self._calibrate = True @@ -225,6 +218,7 @@ def _pre_init(self, **kwargs) -> None: self.sensor_type = SensorType.SAR self.bands = SarBandMap() self.is_ortho = False + self._need_snap = self._need_snap_to_pre_process() def _post_init(self, **kwargs) -> None: """ @@ -234,6 +228,70 @@ def _post_init(self, **kwargs) -> None: self._set_sensor_mode() self.pol_channels = self._get_raw_bands() + @cache + def get_snap_version(self): + try: + return snap.get_snap_version() + except AttributeError: + # TODO: To be removed with sertit >= 1.42 + import subprocess + + from packaging.version import Version + + snap_version = None + try: + output = subprocess.run(["gpt", "--diag"], capture_output=True) + except FileNotFoundError: + raise FileNotFoundError("'gpt' not found in your PATH") + + stdout = output.stdout.decode("utf-8") + + if stdout is not None: + version_str = stdout.split("\n") + try: + version_str = [v for v in version_str if "version" in v][0] + except IndexError as ex: + LOGGER.debug(ex) + else: + snap_version = version_str.split(" ")[-1] + + snap_version = Version(snap_version) + + return snap_version + + @cache + def _has_snap_10_or_higher(self) -> bool: + """True if SNAP version is 10 or higher""" + try: + return misc.compare_version(self.get_snap_version(), "10.0.0", ">=") + except TypeError: + # TODO: To be removed with sertit >= 1.42 + from packaging.version import Version + + misc.compare(self.get_snap_version(), Version("10.0.0"), ">=") + + def _get_predictor(self) -> int: + """ + Get LZW predictor to 1 in order SNAP < 10.0.0 to be able to read this GeoTiff (in dspk operations mostly). + + Caused by: javax.imageio.IIOException: Illegal value for Predictor in TIFF file + + Leave it to None if SNAP is 10 or higher + """ + return None if self._has_snap_10_or_higher() else 1 + + def _need_snap_to_pre_process(self): + """This product needs SNAP for pre-process.""" + raw_band_path = self.get_raw_band_paths() + + # Cannot use get_default_band here, because of recursion + def_key = list(raw_band_path.keys())[0] + with rasterio.open(raw_band_path[def_key]) as ds: + raw_crs = ds.crs + + need_snap = not (raw_crs is not None and raw_crs.is_projected) + return need_snap + @cache @simplify def footprint(self) -> gpd.GeoDataFrame: @@ -668,14 +726,10 @@ def _pre_process_sar(self, band: sab, pixel_size: float = None, **kwargs) -> str else def_pixel_size ) - raw_band_path = str(self.get_raw_band_paths(**kwargs)[band]) - with rasterio.open(raw_band_path) as ds: - raw_crs = ds.crs - - if raw_crs and raw_crs.is_projected: + if not self._need_snap: # Set the nodata and write the image where they belong arr = utils.read( - raw_band_path, + self.get_raw_band_paths(**kwargs)[band], pixel_size=pixel_size if pixel_size != 0 else None, masked=False, ) @@ -690,7 +744,7 @@ def _pre_process_sar(self, band: sab, pixel_size: float = None, **kwargs) -> str file_path, dtype=np.float32, nodata=self._snap_no_data, - predictor=SAR_PREDICTOR, + predictor=self._get_predictor(), ) return file_path else: @@ -915,15 +969,14 @@ def interp_na(array, dim): ) # WARNING: Set nodata to 0 here as it is the value wanted by SNAP ! - # SNAP fails with classic predictor !!! Set the predictor to the default value (1) !!! + # SNAP < 10.0.0 fails with classic predictor !!! Set the predictor to the default value (1) !!! # Caused by: javax.imageio.IIOException: Illegal value for Predictor in TIFF file - # https://forum.step.esa.int/t/exception-found-when-reading-compressed-tif/654/7 utils.write( arr, file_path, dtype=np.float32, nodata=self._snap_no_data, - predictor=SAR_PREDICTOR, + predictor=self._get_predictor(), ) return file_path