def data(sceneid, band, overview_level, nodata, simplify): """Create footprint from data.""" meta = _landsat_parse_scene_id(sceneid) landsat_prefix = os.path.join(LANDSAT_BUCKET, meta["key"]) s3_path = f"{landsat_prefix}_B{band}.TIF" # INFO: we set GDAL_DISABLE_READDIR_ON_OPEN=False to make sure we fetch the ovr with rasterio.Env(GDAL_DISABLE_READDIR_ON_OPEN=False): with rasterio.open(s3_path) as src: if overview_level == 0: decim = 1 else: decim = src.overviews(1)[overview_level - 1] with WarpedVRT(src, nodata=nodata) as vrt: feat = list(dataset_features(vrt, bidx=1, sampling=decim, band=False))[ 0 ] if simplify: g = shape(feat["geometry"]) feat["geometry"] = mapping(g.simplify(0.01)) geom = {"type": "FeatureCollection", "features": [feat]} click.echo(json.dumps(geom))
def test_landsat_id_pre_valid(): """Parse landsat valid pre-collection sceneid and return metadata.""" scene = "LC80300342017083LGN00" expected_content = { "acquisitionJulianDay": "083", "acquisitionYear": "2017", "archiveVersion": "00", "date": "2017-03-24", "groundStationIdentifier": "LGN", "key": "L8/030/034/LC80300342017083LGN00/LC80300342017083LGN00", "path": "030", "row": "034", "satellite": "8", "scene": "LC80300342017083LGN00", "sensor": "C", } assert landsat8._landsat_parse_scene_id(scene) == expected_content
def landsat_get_ang(sceneid): """Get Landsat-8 MTL metadata Attributes ---------- sceneid : str Landsat sceneid. For scenes after May 2017, sceneid have to be LANDSAT_PRODUCT_ID. Returns ------- out : dict returns a JSON like object with the metadata. """ scene_params = _landsat_parse_scene_id(sceneid) s3_key = scene_params["key"] meta_file = f"http://landsat-pds.s3.amazonaws.com/{s3_key}_ANG.txt" metadata = str(urlopen(meta_file).read().decode()) return _parse_ang_txt(metadata)
def tilejson_handler( event: Dict, sceneid: 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}/tiles/{sceneid}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}?{qs}" ) scene_params = landsat8._landsat_parse_scene_id(sceneid) landsat_address = f"{LANDSAT_BUCKET}/{scene_params['key']}_BQA.TIF" with rasterio.open(landsat_address) as src_dst: bounds = warp.transform_bounds(src_dst.crs, "epsg:4326", *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=sceneid, tilejson="2.1.0", tiles=[tile_url], ) return ("OK", "application/json", json.dumps(meta))
def test_landsat_id_c1_valid(): """Parse landsat valid collection1 sceneid and return metadata.""" scene = "LC08_L1TP_005004_20170410_20170414_01_T1" expected_content = { "acquisitionDay": "10", "acquisitionMonth": "04", "acquisitionYear": "2017", "collectionCategory": "T1", "collectionNumber": "01", "date": "2017-04-10", "key": "c1/L8/005/004/LC08_L1TP_005004_20170410_\ 20170414_01_T1/LC08_L1TP_005004_20170410_20170414_01_T1", "path": "005", "processingCorrectionLevel": "L1TP", "processingDay": "14", "processingMonth": "04", "processingYear": "2017", "row": "004", "satellite": "08", "scene": "LC08_L1TP_005004_20170410_20170414_01_T1", "sensor": "C", } assert landsat8._landsat_parse_scene_id(scene) == expected_content
def test_landsat_id_c1_invalid(): """Raises error on invalid collection1 sceneid.""" scene = "LC08_005004_20170410_20170414_01_T1" with pytest.raises(InvalidLandsatSceneId): landsat8._landsat_parse_scene_id(scene)
def test_landsat_id_pre_invalid(): """Raises error on invalid pre-collection.""" scene = "L0300342017083LGN00" with pytest.raises(InvalidLandsatSceneId): landsat8._landsat_parse_scene_id(scene)
def landsat8_tile(sceneid, tile_x, tile_y, tile_z, bands=("4", "3", "2"), tilesize=256, pan=False, percents="", **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 = landsat8._landsat_parse_scene_id(sceneid) meta_data = landsat8._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"]) addresses = ["{}_B{}.TIF".format(landsat_address, band) for band in bands] values = [] percents = percents.split(',') i = 0 for address in addresses: with rasterio.open(address) as src: 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 = numpy.array(d.flatten()) p_start, p_end = numpy.percentile(dflatten[dflatten > 0], (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(wgs_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) 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))) 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) data = numpy.concatenate(data) 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