def main(event, context): """Handler.""" logger.info(event) sceneid = event["sceneid"] band = event["band"] scene_params = _sentinel_parse_scene_id(sceneid) band_key = f"{scene_params['key']}/B{band}.jp2" logger.info(f"Downloading {band_key}") src_path = f"/tmp/{band}.jp2" with open(src_path, "wb") as f: s3.download_fileobj("sentinel-s2-l1c", band_key, f, ExtraArgs={"RequestPayer": "requester"}) logger.info(f"Translating {band_key} to COG") config = dict(NUM_THREADS=100, GDAL_TIFF_OVR_BLOCKSIZE=128) output_profile = { "driver": "GTiff", "interleave": "pixel", "tiled": True, "blockxsize": 256, "blockysize": 256, "compress": "DEFLATE", } cog_path = f"/tmp/{band}.tif" cog_translate( src_path, cog_path, output_profile, nodata=0, in_memory=False, config=config, quiet=True, ) params = { "ACL": "public-read", "Metadata": { "scene": sceneid, "bands": band }, "ContentType": "image/tiff", } key = band_key.replace(".jp2", ".tif") if PREFIX: key = f"{PREFIX}/{key}" logger.info(f"Uploading {key}") with open(cog_path, "rb") as data: s3.upload_fileobj(data, BUCKET, key, ExtraArgs=params)
def test_sentinel_id_valid_strip(): """Parse sentinel-2 valid sceneid with leading 0 and return metadata.""" scene = "S2A_tile_20170323_07SNC_0" expected_content = { "acquisitionDay": "23", "acquisitionMonth": "03", "acquisitionYear": "2017", "key": "tiles/7/S/NC/2017/3/23/0", "lat": "S", "num": "0", "satellite": "A", "scene": "S2A_tile_20170323_07SNC_0", "sensor": "2", "sq": "NC", "utm": "07", } assert sentinel2._sentinel_parse_scene_id(scene) == expected_content
def tilejson_handler( event: Dict, scene: str, tile_format: str = "png", tile_scale: int = 1, **kwargs: Any, ) -> Tuple[str, str, str]: """Handle /tilejson.json requests.""" # HACK token = event["multiValueQueryStringParameters"].get("access_token") if token: kwargs.update(dict(access_token=token[0])) qs = urllib.parse.urlencode(list(kwargs.items())) tile_url = f"{APP.host}/s2/tiles/{scene}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}?{qs}" scene_params = sentinel2._sentinel_parse_scene_id(scene) sentinel_address = "s3://{}/{}/B{}.jp2".format(sentinel2.SENTINEL_BUCKET, scene_params["key"], "04") with rasterio.open(sentinel_address) as src_dst: bounds = warp.transform_bounds(*[src_dst.crs, "epsg:4326"] + list(src_dst.bounds), densify_pts=21) minzoom, maxzoom = get_zooms(src_dst) center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2, minzoom] meta = dict( bounds=bounds, center=center, minzoom=minzoom, maxzoom=maxzoom, name=scene, tilejson="2.1.0", tiles=[tile_url], ) return ("OK", "application/json", json.dumps(meta))
def test_sentinel_id_invalid(): """Raises error on invalid sentinel-2 sceneid.""" scene = "S2A_tile_20170323_17SNC" with pytest.raises(InvalidSentinelSceneId): sentinel2._sentinel_parse_scene_id(scene)
def sentinel2_tile(sceneid, tile_x, tile_y, tile_z, bands=("04", "03", "02"), tilesize=256, percents='', **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 """ 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 = sentinel2._sentinel_parse_scene_id(sceneid) sentinel_address = "{}/{}".format(SENTINEL_BUCKET, scene_params["key"]) addresses = ["{}/B{}.jp2".format(sentinel_address, band) for band in bands] values = [] percents = percents.split(',') i = 0 for address in addresses: with rasterio.open(address) as src: bounds = warp.transform_bounds(src.crs, "epsg:4326", *src.bounds, densify_pts=21) if int(percents[i]) != 0 and int(percents[i + 1]) != 100: overviews = src.overviews(1) if len(overviews) > 0: d = src.read( out_shape=(1, int(src.height / overviews[len(overviews) - 1]), int(src.width / overviews[len(overviews) - 1]))) else: d = src.read() dflatten_full = numpy.array(d.flatten()) dflatten = dflatten_full[dflatten_full > 0] p_start, p_end = numpy.percentile(dflatten, (int(percents[i]), (int(percents[i + 1])))) values.append([p_start, p_end]) else: values.append([None, None]) i += 2 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) # ) return None, None mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) _tiler = partial(utils.tile_read, bounds=tile_bounds, tilesize=tilesize, nodata=0, **kwargs) with futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor: data, masks = zip(*list(executor.map(_tiler, addresses))) mask = numpy.all(masks, axis=0).astype(numpy.uint8) * 255 new_data = list(data) has_modification = False for ds in range(0, len(new_data)): if values[ds][0] is not None and values[ds][1] is not None: has_modification = True new_data[ds] = rescale_intensity(new_data[ds], in_range=(values[ds][0], values[ds][1]), out_range=(0, 255)) if has_modification == True: data = numpy.array(new_data).astype(numpy.uint8) return numpy.concatenate(data), mask
def test_sentinel_newidl2a_valid(): """Parse sentinel-2 valid sceneid and return metadata.""" expected_content = { "acquisitionDay": "29", "acquisitionMonth": "07", "acquisitionYear": "2017", "aws_bucket": "s3://sentinel-s2-l2a", "aws_prefix": "tiles/19/U/DP/2017/7/29/0", "key": "tiles/19/U/DP/2017/7/29/0", "lat": "U", "num": "0", "satellite": "A", "scene": "S2A_L2A_20170729_19UDP_0", "sensor": "2", "sq": "DP", "utm": "19", "processingLevel": "L2A", "preview_file": "R60m/TCI.jp2", "preview_prefix": "R60m", "bands": [ "01", "02", "03", "04", "05", "06", "07", "08", "09", "11", "12", "8A", ], "valid_bands": [ "01", "02", "03", "04", "05", "06", "07", "08", "09", "11", "12", "8A", "AOT", "SCL", "WVP", ], } assert sentinel2._sentinel_parse_scene_id( SENTINEL_SCENE_L2) == expected_content
def test_sentinel_id_valid_strip(): """Parse sentinel-2 valid sceneid with leading 0 and return metadata.""" expected_content = { "acquisitionDay": "23", "acquisitionMonth": "03", "acquisitionYear": "2017", "aws_bucket": "s3://sentinel-s2-l1c", "aws_prefix": "tiles/7/S/NC/2017/3/23/0", "key": "tiles/7/S/NC/2017/3/23/0", "lat": "S", "num": "0", "satellite": "A", "scene": "S2A_tile_20170323_07SNC_0", "sensor": "2", "sq": "NC", "utm": "07", "processingLevel": "L1C", "preview_file": "preview.jp2", "preview_prefix": "preview", "bands": [ "02", "03", "04", "08", "05", "06", "07", "11", "12", "8A", "01", "09", "10", ], "valid_bands": [ "02", "03", "04", "08", "05", "06", "07", "11", "12", "8A", "01", "09", "10", ], } with pytest.warns(DeprecationWarning): assert (sentinel2._sentinel_parse_scene_id("S2A_tile_20170323_07SNC_0") == expected_content)