def tile(sceneid, tile_x, tile_y, tile_z, rgb=('04', '03', '02'), tilesize=256): """Create mercator tile from Sentinel-2 data. Attributes ---------- sceneid : str Sentinel-2 sceneid. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. rgb : tuple, str, optional (default: ('04', '03', '02')) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. Returns ------- data : numpy ndarray mask: numpy array """ if not isinstance(rgb, tuple): rgb = tuple((rgb, )) scene_params = utils.sentinel_parse_scene_id(sceneid) sentinel_address = '{}/{}'.format(SENTINEL_BUCKET, scene_params['key']) sentinel_preview = '{}/preview.jp2'.format(sentinel_address) with rasterio.open(sentinel_preview) as src: wgs_bounds = transform_bounds(*[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds('Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) addresses = ['{}/B{}.jp2'.format(sentinel_address, band) for band in rgb] _tiler = partial(utils.tile_band_worker, bounds=tile_bounds, tilesize=tilesize, nodata=0) with futures.ThreadPoolExecutor(max_workers=3) as executor: data, masks = zip(*list(executor.map(_tiler, addresses))) mask = np.all(masks, axis=0).astype(np.uint8) * 255 return np.concatenate(data), mask
def expression(self, x: int, y: int, z: int, expr: str, tilesize: int = 256, **kwargs: Any) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Read Tile from a COG and apply simple band math. Attributes ---------- x : int X tile map index. y : int y tile map index z : int Z tile map index expr : str Band math expression. tilesize : int, optional Output tile size. Default is 256 kwargs : Any, optional Additional options to forward to rio_tiler.utils._tile_read Returns ------- tile : numpy.ndarray Tile data. mask : numpy.ndarray Mask data. """ if not self.tile_exists(x, y, z): raise TileOutsideBounds( f"Tile {z}/{x}/{y} is outside image bounds") tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z)) bands_names = tuple(set(re.findall(r"b(?P<bands>[0-9A]{1,2})", expr))) expr_bands = ["b{}".format(b) for b in bands_names] indexes = tuple(map(int, bands_names)) tile, mask = _tile_read(self.src_dst, tile_bounds, tilesize, indexes=indexes, **kwargs) tile = dict(zip(expr_bands, tile)) rgb = expr.split(",") return ( numpy.array([ numpy.nan_to_num( numexpr.evaluate(bloc.strip(), local_dict=tile)) for bloc in rgb ]), mask, )
def tile(sceneid, tile_x, tile_y, tile_z, bands=("04", "03", "02"), tilesize=256): """ Create mercator tile from Sentinel-2 data. Attributes ---------- sceneid : str Sentinel-2 sceneid. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. bands : tuple, str, optional (default: ('04', '03', '02')) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. Returns ------- data : numpy ndarray mask: numpy array """ if not isinstance(bands, tuple): bands = tuple((bands,)) for band in bands: if band not in SENTINEL_BANDS: raise InvalidBandName("{} is not a valid Sentinel band name".format(band)) scene_params = _sentinel_parse_scene_id(sceneid) sentinel_address = "{}/{}".format(SENTINEL_BUCKET, scene_params["key"]) sentinel_preview = "{}/preview.jp2".format(sentinel_address) with rasterio.open(sentinel_preview) as src: wgs_bounds = transform_bounds( *[src.crs, "epsg:4326"] + list(src.bounds), densify_pts=21 ) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( "Tile {}/{}/{} is outside image bounds".format(tile_z, tile_x, tile_y) ) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) addresses = ["{}/B{}.jp2".format(sentinel_address, band) for band in bands] _tiler = partial(utils.tile_read, bounds=tile_bounds, tilesize=tilesize, nodata=0) with futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor: data, masks = zip(*list(executor.map(_tiler, addresses))) mask = np.all(masks, axis=0).astype(np.uint8) * 255 return np.concatenate(data), mask
def tile(address, tile_x, tile_y, tile_z, rgb=None, tilesize=256, nodata=None, alpha=None): """Create mercator tile from any images. Attributes ---------- address : str file url. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. rgb : tuple, int, optional (default: (1, 2, 3)) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. nodata: int or float, optional (defaults: None) alpha: int, optional (defaults: None) Force alphaband if not present in the dataset metadata Returns ------- data : numpy ndarray mask: numpy array """ with rasterio.open(address) as src: wgs_bounds = transform_bounds(*[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) nodata = nodata if nodata is not None else src.nodata if not rgb: rgb = src.indexes if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds('Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) return utils.tile_band_worker(address, tile_bounds, tilesize, indexes=rgb, nodata=nodata, alpha=alpha)
def tile(bucket, key, tile_x, tile_y, tile_z, rgb=(1, 2, 3), tilesize=256, prefix='s3:/'): """Create mercator tile from AWS hosted images and encodes it in base64. Attributes ---------- bucket : str AWS bucket's name. key : str AWS file's key. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. tileformat : str Image format to return (Accepted: "jpg" or "png") rgb : tuple, int, optional (default: (1, 2, 3)) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. Returns ------- out : numpy ndarray """ source_address = '{}/{}/{}'.format(prefix, bucket, key) with rasterio.open(source_address) as src: wgs_bounds = transform_bounds( *[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) nodata = src.nodata if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( 'Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) w, s, e, n = tile_bounds out = utils.tile_band_worker(source_address, tile_bounds, tilesize, indexes=rgb, nodata=nodata) return out
def tile(address, tile_x, tile_y, tile_z, indexes=None, tilesize=256, nodata=None): """ Create mercator tile from any images. Attributes ---------- address : str file url. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. indexes : tuple, int, optional (default: (1, 2, 3)) Bands indexes for the RGB combination. tilesize : int, optional (default: 256) Output image size. nodata: int or float, optional Overwrite nodata value for mask creation. Returns ------- data : numpy ndarray mask: numpy array """ with rasterio.open(address) as src: wgs_bounds = transform_bounds(*[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) indexes = indexes if indexes is not None else src.indexes if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( 'Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) return utils.tile_read(src, tile_bounds, tilesize, indexes=indexes, nodata=nodata)
def tile( self, x: int, y: int, z: int, tilesize: int = 256, assets: Optional[List[str]] = None, **kwargs: Any, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Read Tile from a COG. Attributes ---------- x : int X tile map index. y : int y tile map index z : int Z tile map index assets : list or tuple or int, optional Band assets. tilesize : int, optional Output tile size. Default is 256. kwargs : Any, optional Additional options to forward to rio_tiler.utils._tile_read Returns ------- tile : numpy.ndarray Tile data. mask : numpy.ndarray Mask data. """ if isinstance(assets, str): assets = [assets] elif isinstance(assets, tuple): assets = list(assets) if not self.tile_exists(x, y, z): raise TileOutsideBounds( f"Tile {z}/{x}/{y} is outside image bounds") assets = assets if assets else list(self.item["assets"].keys()) assets = [self.item["assets"][asset]["href"] for asset in assets] return self._read_multiple(assets, x, y, z, tilesize=tilesize, **kwargs)
def tile( src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT], x: int, y: int, z: int, tilesize: int = 256, **kwargs, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Read mercator tile from an image. Attributes ---------- src_dst : rasterio.io.DatasetReader rasterio.io.DatasetReader object x : int Mercator tile X index. y : int Mercator tile Y index. z : int Mercator tile ZOOM level. tilesize : int, optional Output tile size. Default is 256. kwargs : Any, optional Additional options to forward to part() Returns ------- data : numpy ndarray mask: numpy array """ bounds = transform_bounds(src_dst.crs, constants.WGS84_CRS, *src_dst.bounds, densify_pts=21) if not tile_exists(bounds, z, x, y): raise TileOutsideBounds(f"Tile {z}/{x}/{y} is outside image bounds") tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z)) return part( src_dst, tile_bounds, tilesize, tilesize, dst_crs=constants.WEB_MERCATOR_CRS, **kwargs, )
def _s1_tiler(src_path): with rasterio.open(src_path) as src_dst: with WarpedVRT( src_dst, src_crs=src_dst.gcps[1], src_transform=transform.from_gcps(src_dst.gcps[0]), src_nodata=0, ) as vrt_dst: if not utils.tile_exists(vrt_dst.bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( "Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) return utils._tile_read(vrt_dst, bounds=tile_bounds, tilesize=tilesize)
def tile( stac: Dict, assets: Sequence[str], tile_x: int, tile_y: int, tile_z: int, tilesize: int = 256, **kwargs: Any, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Create mercator tile from any images. Attributes ---------- stac : dict STAC item. assets : list Asset names. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. tilesize : int, optional (default: 256) Output image size. kwargs: dict, optional These will be passed to the 'rio_tiler.reader.tile' function. Returns ------- data : numpy ndarray mask: numpy array """ if isinstance(assets, str): assets = (assets, ) if not tile_exists(stac["bbox"], tile_z, tile_x, tile_y): raise TileOutsideBounds( f"Tile {tile_z}/{tile_x}/{tile_y} is outside item bounds") assets_url = _get_href(stac, assets) return reader.multi_tile(assets_url, tile_x, tile_y, tile_z, **kwargs)
def tile( self, tile_x: int, tile_y: int, tile_z: int, tilesize: int = 256, indexes: Optional[Sequence] = None, expression: Optional[str] = "", **kwargs: Any, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """Read a TMS map tile from a COG.""" kwargs = {**self._kwargs, **kwargs} if isinstance(indexes, int): indexes = (indexes, ) if expression: indexes = parse_expression(expression) tile = morecantile.Tile(x=tile_x, y=tile_y, z=tile_z) if not self._tile_exists(tile): raise TileOutsideBounds( "Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) tile_bounds = self.tms.xy_bounds(*tile) tile, mask = reader.part( self.dataset, tile_bounds, tilesize, tilesize, dst_crs=self.tms.crs, indexes=indexes, **kwargs, ) if expression: blocks = expression.lower().split(",") bands = [f"b{bidx}" for bidx in indexes] tile = apply_expression(blocks, bands, tile) return tile, mask
def tile( self, x: int, y: int, z: int, tilesize: int = 256, **kwargs: Any, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Read Tile from a COG. Attributes ---------- x : int X tile map index. y : int y tile map index z : int Z tile map index indexes : list or tuple or int, optional Band indexes. tilesize : int, optional Output tile size. Default is 256. kwargs : Any, optional Additional options to forward to rio_tiler.utils._tile_read Returns ------- tile : numpy.ndarray Tile data. mask : numpy.ndarray Mask data. """ if not self.tile_exists(x, y, z): raise TileOutsideBounds( f"Tile {z}/{x}/{y} is outside image bounds") tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z)) return _tile_read(self.src_dst, tile_bounds, tilesize, **kwargs)
def tile(address, tile_x, tile_y, tile_z, tilesize=256, **kwargs): """ Create mercator tile from any images. Attributes ---------- address : str file url. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. tilesize : int, optional (default: 256) Output image size. kwargs: dict, optional These will be passed to the 'rio_tiler.utils._tile_read' function. Returns ------- data : numpy ndarray mask: numpy array """ with rasterio.open(address) as src: bounds = transform_bounds(src.crs, "epsg:4326", *src.bounds, densify_pts=21) if not utils.tile_exists(bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( "Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) return utils.tile_read(src, tile_bounds, tilesize, **kwargs)
def tile(sceneid, tile_x, tile_y, tile_z, rgb=(4, 3, 2), r_bds=(0, 16000), g_bds=(0, 16000), b_bds=(0, 16000), tilesize=256, pan=False): """Create mercator tile from Landsat-8 data and encodes it in base64. Attributes ---------- sceneid : str Landsat sceneid. For scenes after May 2017, sceneid have to be LANDSAT_PRODUCT_ID. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. rgb : tuple, int, optional (default: (4, 3, 2)) Bands index for the RGB combination. r_bds : tuple, int, optional (default: (0, 16000)) First band (red) DN min and max values (DN * 10,000) used for the linear rescaling. g_bds : tuple, int, optional (default: (0, 16000)) Second band (green) DN min and max values (DN * 10,000) used for the linear rescaling. b_bds : tuple, int, optional (default: (0, 16000)) Third band (blue) DN min and max values (DN * 10,000) used for the linear rescaling. tilesize : int, optional (default: 256) Output image size. pan : boolean, optional (default: False) If True, apply pan-sharpening. Returns ------- out : numpy ndarray (type: uint8) """ scene_params = utils.landsat_parse_scene_id(sceneid) meta_data = utils.landsat_get_mtl(sceneid).get('L1_METADATA_FILE') landsat_address = '{}/{}'.format(LANDSAT_BUCKET, scene_params['key']) wgs_bounds = toa_utils._get_bounds_from_metadata( meta_data['PRODUCT_METADATA']) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds('Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) # define a list of bands Min and Max Values (from input) histo_cuts = dict(zip(rgb, [r_bds, g_bds, b_bds])) ms_tile_size = int(tilesize / 2) if pan else tilesize addresses = ['{}_B{}.TIF'.format(landsat_address, band) for band in rgb] _tiler = partial(utils.tile_band_worker, bounds=tile_bounds, tilesize=ms_tile_size) with futures.ThreadPoolExecutor(max_workers=3) as executor: out = np.stack(list(executor.map(_tiler, addresses))) if pan: pan_address = '{}_B8.TIF'.format(landsat_address) matrix_pan = utils.tile_band_worker(pan_address, tile_bounds, tilesize) w, s, e, n = tile_bounds pan_transform = transform.from_bounds(w, s, e, n, tilesize, tilesize) vis_transform = pan_transform * Affine.scale(2.) out = pansharpen(out, vis_transform, matrix_pan, pan_transform, np.int16, 'EPSG:3857', 'EPSG:3857', 0.2, method='Brovey', src_nodata=0) sun_elev = meta_data['IMAGE_ATTRIBUTES']['SUN_ELEVATION'] for bdx, band in enumerate(rgb): multi_reflect = meta_data['RADIOMETRIC_RESCALING'].get( 'REFLECTANCE_MULT_BAND_{}'.format(band)) add_reflect = meta_data['RADIOMETRIC_RESCALING'].get( 'REFLECTANCE_ADD_BAND_{}'.format(band)) out[bdx] = 10000 * reflectance.reflectance( out[bdx], multi_reflect, add_reflect, sun_elev, src_nodata=0) out[bdx] = np.where( out[bdx] > 0, utils.linear_rescale(out[bdx], in_range=histo_cuts.get(band), out_range=[1, 255]), 0) return out.astype(np.uint8)
def part( src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT], bounds: Tuple[float, float, float, float], height: Optional[int] = None, width: Optional[int] = None, padding: int = 0, dst_crs: Optional[CRS] = None, bounds_crs: Optional[CRS] = None, minimum_overlap: Optional[float] = None, warp_vrt_option: Dict = {}, max_size: Optional[int] = None, **kwargs: Any, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Read part of an image. Attributes ---------- src_dst: rasterio.io.DatasetReader rasterio.io.DatasetReader object bounds: tuple Output bounds (left, bottom, right, top) in target crs ("dst_crs"). height: int, optional Output height of the array. width: int, optional Output width of the array. padding: int, optional Padding to apply to each edge of the tile when retrieving data to assist in reducing resampling artefacts along edges. dst_crs: CRS or str, optional Target coordinate reference system, default is "epsg:3857". bounds_crs: CRS or str, optional Overwrite bounds coordinate reference system, default is equal to the output CRS (dst_crs). minimum_tile_cover: float, optional Minimum % overlap for which to raise an error with dataset not covering enought of the tile. warp_vrt_option: dict, optional These will be passed to the rasterio.warp.WarpedVRT class. max_size: int, optional Limit output size array if not widht and height. kwargs: Any, optional Additional options to forward to reader._read() Returns ------- data : numpy ndarray mask: numpy array """ if not dst_crs: dst_crs = src_dst.crs if max_size and width and height: warnings.warn( "'max_size' will be ignored with with 'height' and 'width' set.", UserWarning, ) if bounds_crs: bounds = transform_bounds(bounds_crs, dst_crs, *bounds, densify_pts=21) if minimum_overlap: src_bounds = transform_bounds(src_dst.crs, dst_crs, *src_dst.bounds, densify_pts=21) x_overlap = max( 0, min(src_bounds[2], bounds[2]) - max(src_bounds[0], bounds[0])) y_overlap = max( 0, min(src_bounds[3], bounds[3]) - max(src_bounds[1], bounds[1])) cover_ratio = (x_overlap * y_overlap) / ((bounds[2] - bounds[0]) * (bounds[3] - bounds[1])) if cover_ratio < minimum_overlap: raise TileOutsideBounds( "Dataset covers less than {:.0f}% of tile".format(cover_ratio * 100)) vrt_transform, vrt_width, vrt_height = get_vrt_transform(src_dst, bounds, height, width, dst_crs=dst_crs) window = windows.Window(col_off=0, row_off=0, width=vrt_width, height=vrt_height) if max_size and not (width and height): if max(vrt_width, vrt_height) > max_size: ratio = vrt_height / vrt_width if ratio > 1: height = max_size width = math.ceil(height / ratio) else: width = max_size height = math.ceil(width * ratio) out_height = height or vrt_height out_width = width or vrt_width if padding > 0 and not is_aligned(src_dst, bounds, out_height, out_width): vrt_transform = vrt_transform * Affine.translation(-padding, -padding) orig_vrt_height = vrt_height orig_vrt_width = vrt_width vrt_height = vrt_height + 2 * padding vrt_width = vrt_width + 2 * padding window = windows.Window( col_off=padding, row_off=padding, width=orig_vrt_width, height=orig_vrt_height, ) return _read( src_dst, out_height, out_width, window=window, vrt_options=dict( crs=dst_crs, transform=vrt_transform, width=vrt_width, height=vrt_height, **warp_vrt_option, ), **kwargs, )
def tile(sceneid, tile_x, tile_y, tile_z, bands=None, tilesize=256): """ Create mercator tile from CBERS data. Attributes ---------- sceneid : str CBERS sceneid. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. bands : tuple, int, optional (default: None) Bands index for the RGB combination. If None uses default defined for the instrument tilesize : int, optional (default: 256) Output image size. Returns ------- data : numpy ndarray mask: numpy array """ scene_params = utils.cbers_parse_scene_id(sceneid) if not bands: bands = scene_params["rgb"] if not isinstance(bands, tuple): bands = tuple((bands, )) cbers_address = "{}/{}".format(CBERS_BUCKET, scene_params["key"]) with rasterio.open("{}/{}_BAND{}.tif".format( cbers_address, sceneid, scene_params["reference_band"])) as src: wgs_bounds = transform_bounds(*[src.crs, "epsg:4326"] + list(src.bounds), densify_pts=21) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds("Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) addresses = [ "{}/{}_BAND{}.tif".format(cbers_address, sceneid, band) for band in bands ] _tiler = partial(utils.tile_read, bounds=tile_bounds, tilesize=tilesize, nodata=0) with futures.ThreadPoolExecutor(max_workers=3) as executor: data, masks = zip(*list(executor.map(_tiler, addresses))) mask = np.all(masks, axis=0).astype(np.uint8) * 255 return np.concatenate(data), mask
def tile( sceneid: str, tile_x: int, tile_y: int, tile_z: int, bands: Union[Sequence[str], str] = ("04", "03", "02"), tilesize: int = 256, **kwargs: Dict, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Create mercator tile from Sentinel-2 data. Attributes ---------- sceneid : str Sentinel-2 sceneid. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. bands : tuple, str, optional (default: ('04', '03', '02')) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. kwargs: dict, optional These will be passed to the 'rio_tiler.utils._tile_read' function. Returns ------- data : numpy ndarray mask: numpy array """ if isinstance(bands, str): bands = (bands, ) scene_params = sentinel2_parser(sceneid) for band in bands: if band not in scene_params["valid_bands"]: raise InvalidBandName( "{} is not a valid Sentinel band name".format(band)) sentinel_prefix = "{scheme}://{bucket}/{prefix}".format(**scene_params) preview_file = os.path.join(sentinel_prefix, scene_params["preview_file"]) with rasterio.open(preview_file) as src: bounds = transform_bounds(src.crs, constants.WGS84_CRS, *src.bounds, densify_pts=21) if not tile_exists(bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( "Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) if scene_params["processingLevel"] == "L2A": bands = [_l2_prefixed_band(b) for b in bands] else: bands = ["B{}".format(b) for b in bands] addresses = [f"{sentinel_prefix}/{band}.jp2" for band in bands] return reader.multi_tile(addresses, tile_x, tile_y, tile_z, tilesize=tilesize, nodata=0)
def tile( sceneid: str, tile_x: int, tile_y: int, tile_z: int, bands: Union[Sequence[str], str] = ["4", "3", "2"], tilesize: int = 256, pan: bool = False, **kwargs: Any, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Create mercator tile from Landsat-8 data. Attributes ---------- sceneid : str Landsat sceneid. For scenes after May 2017, sceneid have to be LANDSAT_PRODUCT_ID. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. bands : tuple, str, optional (default: ("4", "3", "2")) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. pan : boolean, optional (default: False) If True, apply pan-sharpening. kwargs: dict, optional These will be passed to the 'rio_tiler.utils._tile_read' function. Returns ------- data : numpy ndarray mask: numpy array """ if isinstance(bands, str): bands = (bands,) for band in bands: if band not in LANDSAT_BANDS: raise InvalidBandName("{} is not a valid Landsat band name".format(band)) scene_params = landsat_parser(sceneid) meta: Dict = _landsat_get_mtl(sceneid)["L1_METADATA_FILE"] landsat_prefix = "{scheme}://{bucket}/{prefix}/{scene}".format(**scene_params) bounds = toa_utils._get_bounds_from_metadata(meta["PRODUCT_METADATA"]) if not tile_exists(bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( "Tile {}/{}/{} is outside image bounds".format(tile_z, tile_x, tile_y) ) def worker(band: str): asset = f"{landsat_prefix}_B{band}.TIF" if band == "QA": nodata = 1 resamp = "nearest" else: nodata = 0 resamp = "bilinear" with rasterio.open(asset) as src_dst: tile, mask = reader.tile( src_dst, tile_x, tile_y, tile_z, tilesize=tilesize, nodata=nodata, resampling_method=resamp, ) return tile, mask with futures.ThreadPoolExecutor(max_workers=constants.MAX_THREADS) as executor: data, masks = zip(*list(executor.map(worker, bands))) data = numpy.concatenate(data) mask = numpy.all(masks, axis=0).astype(numpy.uint8) * 255 if pan: pan_data, mask = worker("8") data = pansharpening_brovey(data, pan_data, 0.2, pan_data.dtype) if bands[0] != "QA" or len(bands) != 1: for bdx, band in enumerate(bands): data[bdx] = _convert(data[bdx], band, meta) return data, mask
def raw( self, x: int, y: int, z: int, indexes: Optional[Union[Tuple, List, int]] = None, tilesize: int = 256, **kwargs: Any, ) -> bytes: """ Read Tile from a COG and render as a GeoTIFF file. Attributes ---------- x : int X tile map index. y : int y tile map index z : int Z tile map index indexes : list or tuple or int, optional Band indexes. tilesize : int, optional Output tile size. Default is 256 kwargs : Any, optional Additional options to forward to rio_tiler.utils._tile_read Returns ------- bytes : GeoTIFF binary content """ if not self.tile_exists(x, y, z): raise TileOutsideBounds( f"Tile {z}/{x}/{y} is outside image bounds") tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z)) if isinstance(indexes, int): indexes = (indexes, ) indexes = indexes if indexes else self.src_dst.indexes tile, alpha = _tile_read(self.src_dst, tile_bounds, tilesize, indexes=indexes, **kwargs) count, _, _ = tile.shape output_profile = dict( driver="GTiff", dtype=tile.dtype, count=count, nodata=self.src_dst.nodata, height=tilesize, width=tilesize, ) options = geotiff_options(x, y, z, tilesize) output_profile.update(options) with MemoryFile() as memfile: with memfile.open(**output_profile) as dst: dst.write(tile, indexes=list(range(1, count + 1))) if has_mask_band(self.src_dst): dst.write_mask(alpha.astype("uint8")) dst.colorinterp = [ self.src_dst.colorinterp[b - 1] for b in indexes ] if self._colormap: dst.write_colormap(1, self._colormap) for i, b in enumerate(indexes): dst.set_band_description(i + 1, self.src_dst.descriptions[b - 1]) dst.update_tags(i + 1, **self.src_dst.tags(b)) dst.update_tags(**self.src_dst.tags()) dst._set_all_scales( [self.src_dst.scales[b - 1] for b in indexes]) dst._set_all_offsets( [self.src_dst.offsets[b - 1] for b in indexes]) return memfile.read()
def tile_utm_source(src, ll_x, ll_y, ur_x, ur_y, indexes=None, tilesize=256, nodata=None, alpha=None, dst_crs='epsg:4326'): """ Create a UTM tile from a :py:class:`rasterio.Dataset` in memory. Arguments --------- src : :py:class:`rasterio.Dataset` Source imagery dataset to tile. ll_x : int or float Lower left x position (i.e. Western bound). ll_y : int or f loat Lower left y position (i.e. Southern bound). ur_x : int or float Upper right x position (i.e. Eastern bound). ur_y : int or float Upper right y position (i.e. Northern bound). indexes : tuple of 3 ints, optional Band indexes for the output. By default, extracts all of the indexes from `src`. tilesize : int, optional Output image X and Y pixel extent. Defaults to ``256``. nodata : int or float, optional Value to use for `nodata` pixels during tiling. By default, uses the existing `nodata` value in `src`. alpha : int, optional Alpha band index for tiling. By default, uses the same band as specified by `src`. dst_crs : str, optional Coordinate reference system for output. Defaults to ``"epsg:4326"``. Returns ------- ``(data, mask, window, window_transform)`` tuple. data : :py:class:`numpy.ndarray` int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple channels, ``(Y, X)`` otherwise. mask : :py:class:`numpy.ndarray` int mask indicating which pixels contain information and which are `nodata`. Pixels containing data have value ``255``, `nodata` pixels have value ``0``. window : :py:class:`rasterio.windows.Window` :py:class:`rasterio.windows.Window` object indicating the raster location of the dataset subregion being returned in `data`. window_transform : :py:class:`affine.Affine` Affine transformation for the window. """ wgs_bounds = transform_bounds(*[src.crs, dst_crs] + list(src.bounds), densify_pts=21) indexes = indexes if indexes is not None else src.indexes tile_bounds = (ll_x, ll_y, ur_x, ur_y) if not utils.tile_exists_utm(wgs_bounds, tile_bounds): raise TileOutsideBounds( 'Tile {}/{}/{}/{} is outside image bounds'.format(tile_bounds)) return utils.tile_read_utm(src, tile_bounds, tilesize, indexes=indexes, nodata=nodata, alpha=alpha, dst_crs=dst_crs)
def _tile_read( src_dst, bounds, tilesize, indexes=None, nodata=None, resampling_method="bilinear", tile_edge_padding=2, dst_crs=CRS({"init": "EPSG:3857"}), bounds_crs=None, minimum_tile_cover=None, warp_vrt_option={}, ): """ Read data and mask. Attributes ---------- src_dst : rasterio.io.DatasetReader rasterio.io.DatasetReader object bounds : list Output bounds (left, bottom, right, top) in target crs ("dst_crs"). tilesize : int Output image size indexes : list of ints or a single int, optional, (defaults: None) If `indexes` is a list, the result is a 3D array, but is a 2D array if it is a band index number. nodata: int or float, optional (defaults: None) resampling_method : str, optional (default: "bilinear") Resampling algorithm. tile_edge_padding : int, optional (default: 2) Padding to apply to each edge of the tile when retrieving data to assist in reducing resampling artefacts along edges. dst_crs: CRS or str, optional Target coordinate reference system (default "epsg:3857"). bounds_crs: CRS or str, optional Overwrite bounds coordinate reference system (default None, equal to dst_crs). minimum_tile_cover: float, optional (default: None) Minimum % overlap for which to raise an error with dataset not covering enought of the tile. warp_vrt_option: dict, optional (default: {}) These will be passed to the rasterio.warp.WarpedVRT class. Returns ------- data : numpy ndarray mask: numpy array """ if isinstance(indexes, int): indexes = [indexes] elif isinstance(indexes, tuple): indexes = list(indexes) if not bounds_crs: bounds_crs = dst_crs bounds = transform_bounds(bounds_crs, dst_crs, *bounds, densify_pts=21) vrt_params = dict(add_alpha=True, crs=dst_crs, resampling=Resampling[resampling_method]) vrt_transform, vrt_width, vrt_height = get_vrt_transform(src_dst, bounds, dst_crs=dst_crs) out_window = windows.Window(col_off=0, row_off=0, width=vrt_width, height=vrt_height) src_bounds = transform_bounds(src_dst.crs, dst_crs, *src_dst.bounds, densify_pts=21) x_overlap = max( 0, min(src_bounds[2], bounds[2]) - max(src_bounds[0], bounds[0])) y_overlap = max( 0, min(src_bounds[3], bounds[3]) - max(src_bounds[1], bounds[1])) cover_ratio = (x_overlap * y_overlap) / ((bounds[2] - bounds[0]) * (bounds[3] - bounds[1])) if minimum_tile_cover and cover_ratio < minimum_tile_cover: raise TileOutsideBounds( "Dataset covers less than {:.0f}% of tile".format(cover_ratio * 100)) if tile_edge_padding > 0: vrt_transform = vrt_transform * Affine.translation( -tile_edge_padding, -tile_edge_padding) orig_vrt_height = vrt_height orig_vrt_width = vrt_width vrt_height = vrt_height + 2 * tile_edge_padding vrt_width = vrt_width + 2 * tile_edge_padding out_window = windows.Window( col_off=tile_edge_padding, row_off=tile_edge_padding, width=orig_vrt_width, height=orig_vrt_height, ) vrt_params.update( dict(transform=vrt_transform, width=vrt_width, height=vrt_height)) indexes = indexes if indexes is not None else src_dst.indexes out_shape = (len(indexes), tilesize, tilesize) nodata = nodata if nodata is not None else src_dst.nodata if nodata is not None: vrt_params.update( dict(nodata=nodata, add_alpha=False, src_nodata=nodata)) if has_alpha_band(src_dst): vrt_params.update(dict(add_alpha=False)) vrt_params.update(warp_vrt_option) with WarpedVRT(src_dst, **vrt_params) as vrt: data = vrt.read( out_shape=out_shape, indexes=indexes, window=out_window, resampling=Resampling[resampling_method], ) mask = vrt.dataset_mask(out_shape=(tilesize, tilesize), window=out_window) return data, mask
def tile( sceneid: str, tile_x: int, tile_y: int, tile_z: int, bands: Union[Sequence[str], str] = None, tilesize: int = 256, **kwargs: Dict, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Create mercator tile from CBERS data. Attributes ---------- sceneid : str CBERS sceneid. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. bands : tuple or list or str, optional Bands index for the RGB combination. If None uses default defined for the instrument tilesize : int, optional Output image size. Default is 256 kwargs: dict, optional These will be passed to the 'rio_tiler.reader.tile' function. Returns ------- data : numpy ndarray mask: numpy array """ if isinstance(bands, str): bands = (bands, ) scene_params = cbers_parser(sceneid) if not bands: bands = scene_params["rgb"] for band in bands: if band not in scene_params["bands"]: raise InvalidBandName( "{} is not a valid band name for {} CBERS instrument".format( band, scene_params["instrument"])) cbers_prefix = "{scheme}://{bucket}/{prefix}/{scene}".format( **scene_params) with rasterio.open("{}_BAND{}.tif".format( cbers_prefix, scene_params["reference_band"])) as src_dst: bounds = transform_bounds(src_dst.crs, constants.WGS84_CRS, *src_dst.bounds, densify_pts=21) if not tile_exists(bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds("Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) addresses = [f"{cbers_prefix}_BAND{band}.tif" for band in bands] return reader.multi_tile(addresses, tile_x, tile_y, tile_z, tilesize=tilesize, nodata=0)
def tile(sceneid, tile_x, tile_y, tile_z, bands=("4", "3", "2"), tilesize=256, pan=False, **kwargs): """ Create mercator tile from Landsat-8 data. Attributes ---------- sceneid : str Landsat sceneid. For scenes after May 2017, sceneid have to be LANDSAT_PRODUCT_ID. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. bands : tuple, str, optional (default: ("4", "3", "2")) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. pan : boolean, optional (default: False) If True, apply pan-sharpening. kwargs: dict, optional These will be passed to the 'rio_tiler.utils._tile_read' function. Returns ------- data : numpy ndarray mask: numpy array """ if not isinstance(bands, tuple): bands = tuple((bands, )) for band in bands: if band not in LANDSAT_BANDS: raise InvalidBandName( "{} is not a valid Landsat band name".format(band)) scene_params = _landsat_parse_scene_id(sceneid) meta_data = _landsat_get_mtl(sceneid).get("L1_METADATA_FILE") landsat_address = "{}/{}".format(LANDSAT_BUCKET, scene_params["key"]) wgs_bounds = toa_utils._get_bounds_from_metadata( meta_data["PRODUCT_METADATA"]) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds("Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) def _tiler(band): address = "{}_B{}.TIF".format(landsat_address, band) if band == "QA": nodata = 1 else: nodata = 0 return utils.tile_read(address, bounds=tile_bounds, tilesize=tilesize, nodata=nodata, **kwargs) with futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor: data, masks = zip(*list(executor.map(_tiler, bands))) data = np.concatenate(data) mask = np.all(masks, axis=0).astype(np.uint8) * 255 if pan: pan_address = "{}_B8.TIF".format(landsat_address) matrix_pan, mask = utils.tile_read(pan_address, tile_bounds, tilesize, nodata=0) data = utils.pansharpening_brovey(data, matrix_pan, 0.2, matrix_pan.dtype) sun_elev = meta_data["IMAGE_ATTRIBUTES"]["SUN_ELEVATION"] for bdx, band in enumerate(bands): if band in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]: # OLI multi_reflect = meta_data["RADIOMETRIC_RESCALING"].get( "REFLECTANCE_MULT_BAND_{}".format(band)) add_reflect = meta_data["RADIOMETRIC_RESCALING"].get( "REFLECTANCE_ADD_BAND_{}".format(band)) data[bdx] = 10000 * reflectance.reflectance( data[bdx], multi_reflect, add_reflect, sun_elev) elif band in ["10", "11"]: # TIRS multi_rad = meta_data["RADIOMETRIC_RESCALING"].get( "RADIANCE_MULT_BAND_{}".format(band)) add_rad = meta_data["RADIOMETRIC_RESCALING"].get( "RADIANCE_ADD_BAND_{}".format(band)) k1 = meta_data["TIRS_THERMAL_CONSTANTS"].get( "K1_CONSTANT_BAND_{}".format(band)) k2 = meta_data["TIRS_THERMAL_CONSTANTS"].get( "K2_CONSTANT_BAND_{}".format(band)) data[bdx] = brightness_temp.brightness_temp( data[bdx], multi_rad, add_rad, k1, k2) return data, mask
def tile(sceneid, tile_x, tile_y, tile_z, rgb=('04', '03', '02'), r_bds=(0, 16000), g_bds=(0, 16000), b_bds=(1, 16000), tilesize=256): """Create mercator tile from Sentinel-2 data and encodes it in base64. Attributes ---------- sceneid : str Sentinel-2 sceneid. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. rgb : tuple, int, optional (default: ('04', '03', '02')) Bands index for the RGB combination. r_bds : tuple, int, optional (default: (0, 16000)) First band (red) DN min and max values (DN * 10,000) used for the linear rescaling. g_bds : tuple, int, optional (default: (0, 16000)) Second band (green) DN min and max values (DN * 10,000) used for the linear rescaling. b_bds : tuple, int, optional (default: (0, 16000)) Third band (blue) DN min and max values (DN * 10,000) used for the linear rescaling. tilesize : int, optional (default: 256) Output image size. Returns ------- out : numpy ndarray (type: uint8) """ scene_params = utils.sentinel_parse_scene_id(sceneid) sentinel_address = '{}/{}'.format(SENTINEL_BUCKET, scene_params['key']) sentinel_preview = '{}/preview.jp2'.format(sentinel_address) with rasterio.open(sentinel_preview) as src: wgs_bounds = transform_bounds( *[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds('Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) # define a list of bands Min and Max Values (form input) histo_cuts = dict(zip(rgb, [r_bds, g_bds, b_bds])) addresses = ['{}/B{}.jp2'.format(sentinel_address, band) for band in rgb] _tiler = partial(utils.tile_band_worker, bounds=tile_bounds, tilesize=tilesize) with futures.ThreadPoolExecutor(max_workers=3) as executor: out = np.stack(executor.map(_tiler, addresses)) for bdx, band in enumerate(rgb): # Rescale Intensity to byte (1->255) with 0 being NoData out[bdx] = np.where( out[bdx] > 0, utils.linear_rescale(out[bdx], in_range=histo_cuts.get(band), out_range=[1, 255]), 0) return out.astype(np.uint8)
def tile(sceneid, tile_x, tile_y, tile_z, rgb=(4, 3, 2), tilesize=256, pan=False): """Create mercator tile from Landsat-8 data. Attributes ---------- sceneid : str Landsat sceneid. For scenes after May 2017, sceneid have to be LANDSAT_PRODUCT_ID. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. rgb : tuple, int, optional (default: (4, 3, 2)) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. pan : boolean, optional (default: False) If True, apply pan-sharpening. Returns ------- data : numpy ndarray mask: numpy array """ if not isinstance(rgb, tuple): rgb = tuple((rgb, )) scene_params = utils.landsat_parse_scene_id(sceneid) meta_data = utils.landsat_get_mtl(sceneid).get('L1_METADATA_FILE') landsat_address = '{}/{}'.format(LANDSAT_BUCKET, scene_params['key']) wgs_bounds = toa_utils._get_bounds_from_metadata( meta_data['PRODUCT_METADATA']) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( 'Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) ms_tile_size = int(tilesize / 2) if pan else tilesize addresses = ['{}_B{}.TIF'.format(landsat_address, band) for band in rgb] _tiler = partial(utils.tile_band_worker, bounds=tile_bounds, tilesize=ms_tile_size, nodata=0) with futures.ThreadPoolExecutor(max_workers=3) as executor: data, masks = zip(*list(executor.map(_tiler, addresses))) data = np.concatenate(data) mask = np.all(masks, axis=0).astype(np.uint8) * 255 if pan: pan_address = '{}_B8.TIF'.format(landsat_address) matrix_pan, mask = utils.tile_band_worker(pan_address, tile_bounds, tilesize, nodata=0) w, s, e, n = tile_bounds pan_transform = transform.from_bounds(w, s, e, n, tilesize, tilesize) vis_transform = pan_transform * Affine.scale(2.) data = pansharpen(data, vis_transform, matrix_pan, pan_transform, np.int16, 'EPSG:3857', 'EPSG:3857', 0.2, method='Brovey', src_nodata=0) sun_elev = meta_data['IMAGE_ATTRIBUTES']['SUN_ELEVATION'] for bdx, band in enumerate(rgb): if int(band) > 9: # TIRS multi_rad = meta_data['RADIOMETRIC_RESCALING'].get( 'RADIANCE_MULT_BAND_{}'.format(band)) add_rad = meta_data['RADIOMETRIC_RESCALING'].get( 'RADIANCE_ADD_BAND_{}'.format(band)) k1 = meta_data['TIRS_THERMAL_CONSTANTS'].get( 'K1_CONSTANT_BAND_{}'.format(band)) k2 = meta_data['TIRS_THERMAL_CONSTANTS'].get( 'K2_CONSTANT_BAND_{}'.format(band)) data[bdx] = brightness_temp.brightness_temp( data[bdx], multi_rad, add_rad, k1, k2) else: multi_reflect = meta_data['RADIOMETRIC_RESCALING'].get( 'REFLECTANCE_MULT_BAND_{}'.format(band)) add_reflect = meta_data['RADIOMETRIC_RESCALING'].get( 'REFLECTANCE_ADD_BAND_{}'.format(band)) data[bdx] = 10000 * reflectance.reflectance( data[bdx], multi_reflect, add_reflect, sun_elev) return data, mask
def tile(sceneid, tile_x, tile_y, tile_z, bands=("04", "03", "02"), tilesize=256, **kwargs): """ Create mercator tile from Sentinel-2 data. Attributes ---------- sceneid : str Sentinel-2 sceneid. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. bands : tuple, str, optional (default: ('04', '03', '02')) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. kwargs: dict, optional These will be passed to the 'rio_tiler.utils._tile_read' function. Returns ------- data : numpy ndarray mask: numpy array """ scene_params = _sentinel_parse_scene_id(sceneid) if not isinstance(bands, tuple): bands = tuple((bands, )) for band in bands: if band not in scene_params["valid_bands"]: raise InvalidBandName( "{} is not a valid Sentinel band name".format(band)) preview_file = os.path.join( scene_params["aws_bucket"], scene_params["aws_prefix"], scene_params["preview_file"], ) with rasterio.open(preview_file) as src: bounds = transform_bounds(src.crs, "epsg:4326", *src.bounds, densify_pts=21) if not utils.tile_exists(bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds("Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) path_prefix = os.path.join(scene_params["aws_bucket"], scene_params["aws_prefix"]) if scene_params["processingLevel"] == "L2A": bands = [_l2_prefixed_band(b) for b in bands] else: bands = ["B{}".format(b) for b in bands] def _read_tile(path): with rasterio.open(path) as src_dst: return utils.tile_read(src_dst, bounds=tile_bounds, tilesize=tilesize, nodata=0, **kwargs) addresses = ["{}/{}.jp2".format(path_prefix, band) for band in bands] with futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor: data, masks = zip(*list(executor.map(_read_tile, addresses))) mask = np.all(masks, axis=0).astype(np.uint8) * 255 return np.concatenate(data), mask
def tile(sceneid, tile_x, tile_y, tile_z, bands=("4", "3", "2"), tilesize=256, pan=False): """ Create mercator tile from Landsat-8 data. Attributes ---------- sceneid : str Landsat sceneid. For scenes after May 2017, sceneid have to be LANDSAT_PRODUCT_ID. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. bands : tuple, str, optional (default: ("4", "3", "2")) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. pan : boolean, optional (default: False) If True, apply pan-sharpening. Returns ------- data : numpy ndarray mask: numpy array """ if not isinstance(bands, tuple): bands = tuple((bands, )) for band in bands: if band not in LANDSAT_BANDS: raise InvalidBandName( "{} is not a valid Landsat band name".format(band)) scene_params = _landsat_parse_scene_id(sceneid) meta_data = _landsat_get_mtl(sceneid).get("L1_METADATA_FILE") landsat_address = "{}/{}".format(LANDSAT_BUCKET, scene_params["key"]) wgs_bounds = toa_utils._get_bounds_from_metadata( meta_data["PRODUCT_METADATA"]) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds("Tile {}/{}/{} is outside image bounds".format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) ms_tile_size = int(tilesize / 2) if pan else tilesize addresses = ["{}_B{}.TIF".format(landsat_address, band) for band in bands] _tiler = partial(utils.tile_read, bounds=tile_bounds, tilesize=ms_tile_size, nodata=0) with futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor: data, masks = zip(*list(executor.map(_tiler, addresses))) data = np.concatenate(data) mask = np.all(masks, axis=0).astype(np.uint8) * 255 if pan: pan_address = "{}_B8.TIF".format(landsat_address) matrix_pan, mask = utils.tile_read(pan_address, tile_bounds, tilesize, nodata=0) w, s, e, n = tile_bounds pan_transform = transform.from_bounds(w, s, e, n, tilesize, tilesize) vis_transform = pan_transform * Affine.scale(2.0) data = pansharpen( data, vis_transform, matrix_pan, pan_transform, np.int16, "EPSG:3857", "EPSG:3857", 0.2, method="Brovey", src_nodata=0, ) sun_elev = meta_data["IMAGE_ATTRIBUTES"]["SUN_ELEVATION"] for bdx, band in enumerate(bands): if int(band) > 9: # TIRS multi_rad = meta_data["RADIOMETRIC_RESCALING"].get( "RADIANCE_MULT_BAND_{}".format(band)) add_rad = meta_data["RADIOMETRIC_RESCALING"].get( "RADIANCE_ADD_BAND_{}".format(band)) k1 = meta_data["TIRS_THERMAL_CONSTANTS"].get( "K1_CONSTANT_BAND_{}".format(band)) k2 = meta_data["TIRS_THERMAL_CONSTANTS"].get( "K2_CONSTANT_BAND_{}".format(band)) data[bdx] = brightness_temp.brightness_temp( data[bdx], multi_rad, add_rad, k1, k2) else: multi_reflect = meta_data["RADIOMETRIC_RESCALING"].get( "REFLECTANCE_MULT_BAND_{}".format(band)) add_reflect = meta_data["RADIOMETRIC_RESCALING"].get( "REFLECTANCE_ADD_BAND_{}".format(band)) data[bdx] = 10000 * reflectance.reflectance( data[bdx], multi_reflect, add_reflect, sun_elev) return data, mask