Esempio n. 1
0
    def _pre_process_sar(self, resolution: float = None) -> dict:
        """
        Pre-process SAR data (geocoding...)

        Args:
            resolution (float): Resolution

        Returns:
            dict: Dictionary containing {band: path}
        """
        out = {}

        # Create target dir (tmp dir)
        with tempfile.TemporaryDirectory() as tmp_dir:
            # Set command as a list
            target_file = os.path.join(tmp_dir, f"{self.condensed_name}")

            # Use dimap for speed and security (ie. GeoTiff's broken georef)
            pp_target = f"{target_file}"
            pp_dim = pp_target + ".dim"

            # Pre-process graph
            if PP_GRAPH not in os.environ:
                sat = "s1" if self.sat_id == Platform.S1.name else "sar"
                spt = "grd" if self.sar_prod_type == SarProductType.GDRG else "cplx"
                pp_graph = os.path.join(utils.get_data_dir(),
                                        f"{spt}_{sat}_preprocess_default.xml")
            else:
                pp_graph = os.environ[PP_GRAPH]
                if not os.path.isfile(pp_graph) or not pp_graph.endswith(
                        ".xml"):
                    FileNotFoundError(f"{pp_graph} cannot be found.")

            # Command line
            if not os.path.isfile(pp_dim):
                def_res = float(os.environ.get(SAR_DEF_RES, self.resolution))
                res_m = resolution if resolution else def_res
                res_deg = res_m / 10.0 * 8.983152841195215e-5  # Approx
                cmd_list = snap.get_gpt_cli(
                    pp_graph,
                    [
                        f"-Pfile={strings.to_cmd_string(self._snap_path)}",
                        f"-Pout={pp_dim}",
                        f"-Pcrs={self.crs()}",
                        f"-Pres_m={res_m}",
                        f"-Pres_deg={res_deg}",
                    ],
                    display_snap_opt=LOGGER.level == logging.DEBUG,
                )

                # Pre-process SAR images according to the given graph
                LOGGER.debug("Pre-process SAR image")
                misc.run_cli(cmd_list)

            # Convert DIMAP images to GeoTiff
            for pol in self.pol_channels:
                # Speckle image
                out[sbn.from_value(pol)] = self._write_sar(pp_dim, pol.value)

        return out
Esempio n. 2
0
    def _run_s3_gpt_cli(self, out_dim: str, resolution: float = None) -> list:
        """
        Construct GPT command line to reproject S3 images and quality flags

        Args:
            out_dim (str): Out DIMAP name
            resolution (float): Resolution

        Returns:
            list: Processed band name
        """
        # Default resolution
        def_res = os.environ.get(S3_DEF_RES, self.resolution)

        # Construct GPT graph
        graph_path = os.path.join(utils.get_data_dir(), "preprocess_s3.xml")
        snap_bands = ",".join([
            self._get_snap_band_name(band)
            for band, band_nb in self.band_names.items() if band_nb
        ])
        if self._instrument_name == S3Instrument.OLCI:
            sensor = "OLCI"
            fmt = "Sen3"
            snap_bands += ",quality_flags"
        else:
            sensor = "SLSTR_500m"
            fmt = "Sen3_SLSTRL1B_500m"
            exception_bands = ",".join([
                self._get_slstr_quality_flags_name(band)
                for band, band_nb in self.band_names.items() if band_nb
            ])
            snap_bands += f",{exception_bands},cloud_an,cloud_in"

        # Run GPT graph
        cmd_list = snap.get_gpt_cli(
            graph_path,
            [
                f"-Pin={strings.to_cmd_string(self.path)}",
                f"-Pbands={snap_bands}",
                f"-Psensor={sensor}",
                f"-Pformat={fmt}",
                f"-Pno_data={self._snap_no_data}",
                f"-Pres_m={resolution if resolution else def_res}",
                f"-Pout={strings.to_cmd_string(out_dim)}",
            ],
            display_snap_opt=LOGGER.level == logging.DEBUG,
        )
        LOGGER.debug("Converting %s", self.name)
        misc.run_cli(cmd_list)

        return snap_bands.split(",")
Esempio n. 3
0
    def _compute_slope(
        self,
        dem_path: str = "",
        resolution: Union[float, tuple] = None,
        size: Union[list, tuple] = None,
        resampling: Resampling = Resampling.bilinear,
    ) -> str:
        """
        Compute slope mask

        Args:
            dem_path (str): DEM path, using EUDEM/MERIT DEM if none
            resolution (Union[float, tuple]): Resolution in meters. If not specified, use the product resolution.
            size (Union[tuple, list]): Size of the array (width, height). Not used if resolution is provided.
            resampling (Resampling): Resampling method

        Returns:
            str: Slope mask path

        """
        # Warp DEM
        warped_dem_path = self._warp_dem(dem_path, resolution, size,
                                         resampling)

        # Get slope path
        slope_dem = os.path.join(self.output,
                                 f"{self.condensed_name}_SLOPE.tif")
        if os.path.isfile(slope_dem):
            LOGGER.debug(
                "Already existing slope DEM for %s. Skipping process.",
                self.name)
        else:
            LOGGER.debug("Computing slope for %s", self.name)
            cmd_slope = [
                "gdaldem",
                "--config",
                "NUM_THREADS",
                MAX_CORES,
                "slope",
                "-compute_edges",
                strings.to_cmd_string(warped_dem_path),
                strings.to_cmd_string(slope_dem),
                "-p",
            ]

            # Run command
            misc.run_cli(cmd_slope)

        return slope_dem
Esempio n. 4
0
    def _despeckle_sar(self, band: sbn) -> str:
        """
        Pre-process SAR data (geocode...)

        Args:
            band (sbn): Band to despeckle

        Returns:
            str: Despeckled path
        """
        # Create target dir (tmp dir)
        with tempfile.TemporaryDirectory() as tmp_dir:
            # Out files
            target_file = os.path.join(tmp_dir, f"{self.condensed_name}")
            dspk_dim = target_file + ".dim"

            # Despeckle graph
            if DSPK_GRAPH not in os.environ:
                dspk_graph = os.path.join(utils.get_data_dir(),
                                          "sar_despeckle_default.xml")
            else:
                dspk_graph = os.environ[DSPK_GRAPH]
                if not os.path.isfile(dspk_graph) or not dspk_graph.endswith(
                        ".xml"):
                    FileNotFoundError(f"{dspk_graph} cannot be found.")

            # Create command line and run it
            if not os.path.isfile(dspk_dim):
                path = self.get_band_paths([band])[band]
                cmd_list = snap.get_gpt_cli(
                    dspk_graph,
                    [f"-Pfile={path}", f"-Pout={dspk_dim}"],
                    display_snap_opt=False,
                )

                # Pre-process SAR images according to the given graph
                LOGGER.debug("Despeckle SAR image")
                misc.run_cli(cmd_list)

            # Convert DIMAP images to GeoTiff
            out = self._write_sar(dspk_dim, band.value, dspk=True)

        return out
Esempio n. 5
0
    def wgs84_extent(self) -> gpd.GeoDataFrame:
        """
        Get the WGS84 extent of the file before any reprojection.
        This is useful when the SAR pre-process has not been done yet.

        .. code-block:: python

            >>> from eoreader.reader import Reader
            >>> path = r"S1A_IW_GRDH_1SDV_20191215T060906_20191215T060931_030355_0378F7_3696.zip"
            >>> prod = Reader().open(path)
            >>> prod.wgs84_extent()
                                   Name  ...                                           geometry
            0  Sentinel-1 Image Overlay  ...  POLYGON ((0.85336 42.24660, -2.32032 42.65493,...
            [1 rows x 12 columns]

        Returns:
            gpd.GeoDataFrame: WGS84 extent as a gpd.GeoDataFrame

        """
        tmp_dir = tempfile.TemporaryDirectory()

        try:
            # Open the map-overlay file
            if self.is_archived:
                # We need to extract the file here as we need a proper file
                with zipfile.ZipFile(self.path, "r") as zip_ds:
                    filenames = [f.filename for f in zip_ds.filelist]
                    regex = re.compile(".*preview.*map-overlay.kml")
                    preview_overlay = zip_ds.extract(
                        list(filter(regex.match, filenames))[0], tmp_dir.name
                    )
            else:
                preview_overlay = os.path.join(self.path, "preview", "map-overlay.kml")

            if os.path.isfile(preview_overlay):
                # Open the KML file
                vectors.set_kml_driver()
                extent_wgs84 = gpd.read_file(preview_overlay)

                if extent_wgs84.empty:
                    # Convert KML to GeoJSON
                    gj_preview_overlay = preview_overlay.replace("kml", "geojson")
                    cmd_line = [
                        "ogr2ogr",
                        "-fieldTypeToString DateTime",  # Disable warning
                        "-f GeoJSON",
                        strings.to_cmd_string(gj_preview_overlay),
                        strings.to_cmd_string(preview_overlay),
                    ]
                    misc.run_cli(cmd_line)

                    # Open the geojson
                    extent_wgs84 = gpd.read_file(gj_preview_overlay)

                    if extent_wgs84.empty:
                        raise InvalidProductError(
                            f"Cannot determine the WGS84 extent of {self.name}"
                        )
            else:
                raise InvalidProductError(
                    f"Impossible to find the map-overlay.kml in {self.path}"
                )

        except Exception as ex:
            raise InvalidProductError(ex) from ex

        finally:
            tmp_dir.cleanup()

        return extent_wgs84
Esempio n. 6
0
    def _compute_hillshade(
        self,
        dem_path: str = "",
        resolution: Union[float, tuple] = None,
        size: Union[list, tuple] = None,
        resampling: Resampling = Resampling.bilinear,
    ) -> str:
        """
        Compute Hillshade mask

        Args:
            dem_path (str): DEM path, using EUDEM/MERIT DEM if none
            resolution (Union[float, tuple]): Resolution in meters. If not specified, use the product resolution.
            size (Union[tuple, list]): Size of the array (width, height). Not used if resolution is provided.
            resampling (Resampling): Resampling method

        Returns:
            str: Hillshade mask path

        """
        # Warp DEM
        warped_dem_path = self._warp_dem(dem_path, resolution, size,
                                         resampling)

        # Get Hillshade path
        hillshade_dem = os.path.join(self.output,
                                     f"{self.condensed_name}_HILLSHADE.tif")
        if os.path.isfile(hillshade_dem):
            LOGGER.debug(
                "Already existing hillshade DEM for %s. Skipping process.",
                self.name)
        else:
            LOGGER.debug("Computing hillshade DEM for %s", self.name)

            # Get angles
            mean_azimuth_angle, mean_zenith_angle = self.get_mean_sun_angles()

            # Altitude of the light, in degrees. 90 if the light comes from above the DEM, 0 if it is raking light.
            alt = 90 - mean_zenith_angle

            # Run cmd
            cmd_hillshade = [
                "gdaldem",
                "--config",
                "NUM_THREADS",
                MAX_CORES,
                "hillshade",
                strings.to_cmd_string(warped_dem_path),
                "-compute_edges",
                "-z",
                "1",
                "-az",
                mean_azimuth_angle,
                "-alt",
                alt,
                "-of",
                "GTiff",
                strings.to_cmd_string(hillshade_dem),
            ]
            # Run command
            misc.run_cli(cmd_hillshade)

        return hillshade_dem