def _read_band( self, path: str, band: BandNames = None, resolution: Union[tuple, list, float] = None, size: Union[list, tuple] = None, ) -> XDS_TYPE: """ Read band from disk. .. WARNING:: Invalid pixels are not managed here Args: path (str): Band path band (BandNames): Band to read resolution (Union[tuple, list, float]): Resolution of the wanted band, in dataset resolution unit (X, Y) size (Union[tuple, list]): Size of the array (width, height). Not used if resolution is provided. Returns: XDS_TYPE: Band xarray """ # Read band band_xda = rasters.read(path, resolution=resolution, size=size, resampling=Resampling.bilinear) # Compute the correct radiometry of the band original_dtype = band_xda.encoding.get("dtype", band_xda.dtype) if original_dtype == "uint16": band_xda /= 10000.0 return band_xda
def _preprocess_s3(self, resolution: float = None): """ pre-process S3 bands (orthorectify...) Args: resolution (float): Resolution Returns: dict: Dictionary containing {band: path} """ band_paths = {} # DIM in tmp files with tempfile.TemporaryDirectory() as tmp_dir: # out_dim = os.path.join(self.output, self.condensed_name + ".dim") # DEBUG OPTION out_dim = os.path.join(tmp_dir, self.condensed_name + ".dim") # Run GPT graph processed_bands = self._run_s3_gpt_cli(out_dim, resolution) # Save all processed bands and quality flags into GeoTIFFs for snap_band_name in processed_bands: # Get standard band names band_name = self._get_band_filename(snap_band_name) # Remove tif if already existing # (if we are here, sth has failed when creating them, so delete them all) out_tif = os.path.join(self.output, band_name + ".tif") if os.path.isfile(out_tif): files.remove(out_tif) # Convert to geotiffs and set no data with only keeping the first band arr = rasters.read( rasters.get_dim_img_path(out_dim, snap_band_name)) arr = arr.where(arr != self._snap_no_data, np.nan) rasters.write(arr, out_tif, dtype=np.float32) # Get the wanted bands (not the quality flags here !) for band in processed_bands: filename = self._get_band_filename(band) if "exception" not in filename: out_tif = os.path.join(self.output, filename + ".tif") if not os.path.isfile(out_tif): raise FileNotFoundError( f"Error when processing S3 bands with SNAP. Couldn't find {out_tif}" ) # Quality flags will crash here try: band_paths[self._get_band_from_filename( filename)] = out_tif except ValueError: pass return band_paths
def footprint(self) -> gpd.GeoDataFrame: """ Get real footprint of the products (without nodata, in french == emprise utile) .. WARNING:: As Landsat 7 is broken (with nodata stripes all over the bands), the footprint is not easily computed and may take some time to be delivered. .. code-block:: python >>> from eoreader.reader import Reader >>> path = r"LC08_L1GT_023030_20200518_20200527_01_T2" >>> prod = Reader().open(path) >>> prod.footprint() index geometry 0 0 POLYGON ((366165.000 4899735.000, 366165.000 4... Overload of the generic function because landsat nodata seems to be different in QA than in regular bands. Indeed, nodata pixels vary according to the band sensor footprint, whereas QA nodata is where at least one band has nodata. We chose to keep QA nodata values for the footprint in order to show where all bands are valid. **TL;DR: We use the QA nodata value to determine the product's footprint**. Returns: gpd.GeoDataFrame: Footprint as a GeoDataFrame """ LOGGER.warning( "Due to the Landsat-7 gaps, this function returns a rounded footprint on the corners. " "Sorry for the inconvenience.") # Read the file with a very low resolution -> use raster_rio that is faster ! gap_msk = rasters.read( self._get_path(self._nodata_band_id), resolution=self.resolution * 50, masked=False, ) # Vectorize the nodata band # Take the convex hull to discard the stripes of L7 to simplify the geometries footprint = rasters.vectorize(gap_msk, values=1, keep_values=False, dissolve=True) # Needs a dataframe to be dissolved footprint = footprint.convex_hull return gpd.GeoDataFrame(geometry=footprint.geometry, crs=footprint.crs)
def _read_band( self, path: str, resolution: Union[tuple, list, float] = None, size: Union[list, tuple] = None, ) -> XDS_TYPE: """ Read band from a dataset. .. WARNING:: Invalid pixels are not managed here ! Args: path (str): Band path resolution (Union[tuple, list, float]): Resolution of the wanted band, in dataset resolution unit (X, Y) size (Union[tuple, list]): Size of the array (width, height). Not used if resolution is provided. Returns: XDS_TYPE: Radiometrically coherent band, saved as float 32 and its metadata """ # Get band name: the last number of the filename: # ie: 'LC08_L1TP_200030_20191218_20191226_01_T1_B1' if self.is_archived: filename = files.get_filename(path.split("!")[-1]) else: filename = files.get_filename(path) band_name = filename[-1] if self._quality_id in filename or self._nodata_band_id in filename: band = rasters.read( path, resolution=resolution, size=size, resampling=Resampling.nearest, # NEAREST TO KEEP THE FLAGS masked=False, ).astype(np.uint16) else: # Read band (call superclass generic method) band = rasters.read(path, resolution=resolution, size=size, resampling=Resampling.bilinear).astype( np.float32) # Open mtd mtd_data = self.read_mtd(force_pd=True) # Get band nb and corresponding coeff c_mul_str = "REFLECTANCE_MULT_BAND_" + band_name c_add_str = "REFLECTANCE_ADD_BAND_" + band_name # Get coeffs to convert DN to reflectance c_mul = mtd_data[c_mul_str].value c_add = mtd_data[c_add_str].value # Manage NULL values try: c_mul = float(c_mul) except ValueError: c_mul = 1 try: c_add = float(c_add) except ValueError: c_add = 0 # Compute the correct radiometry of the band and set no data to 0 band = c_mul * band + c_add # Already in float return band