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
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(",")
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
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
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
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