from pystac_client import Client
from collections import defaultdict
import json
import geopandas
import geoviews as gv
from cartopy import crs
import matplotlib.pyplot as plt
from datetime import datetime
import os
import requests
import boto3
import numpy as np
import xarray as xr
import rasterio as rio
from rasterio.session import AWSSession
from rasterio.plot import show
import rioxarray
import geoviews as gv
import hvplot.xarray
import holoviews as hv
from tqdm import tqdm
from pprint import pprint
'bokeh', 'matplotlib') gv.extension(
From the PO.DAAC Cookbook, to access the GitHub version of the notebook, follow this link.
Estimating Reservoir Surface Area From Harmonized Landsat-Sentinel (HLS) Imagery – Cloud Version
Author: Matthew Bonnema (JPL)
Initiate Data Search
= 'https://cmr.earthdata.nasa.gov/stac'
STAC_URL = Client.open(STAC_URL)
provider_cat = Client.open(f'{STAC_URL}/LPCLOUD/')
catalog #collections = ['HLSL30.v2.0', 'HLSS30.v2.0']
= ['HLSL30.v2.0'] collections
Define Date Range and Region of Interest
= "2021-01/2022-01"
date_range = {
roi "type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-121.60835266113281,
39.49874248613119
],
[-121.26983642578124,
39.49874248613119
],
[-121.26983642578124,
39.756824261131406
],
[-121.60835266113281,
39.756824261131406
],
[-121.60835266113281,
39.49874248613119
]
]
]
}'geometry']
}[= gv.tile_sources.EsriImagery.opts(width=650, height=500)
base = gv.Polygons(roi['coordinates']).opts(line_color='yellow', line_width=10, color=None)
Reservoir * base Reservoir
Search for HLS imagery matching search criteria
= catalog.search(
search =collections,
collections=roi,
intersects=date_range,
datetime=100
limit
)
= search.get_all_items()
item_collection search.matched()
Filter imagery for low cloud images and identify image bands needed for water classification
= ['B8A', 'B03'] # S30 bands for NDWI calculation and quality filtering -> NIR, GREEN, Quality
s30_bands = ['B05', 'B03'] # L30 bands for NDWI calculation and quality filtering -> NIR, GREEN, Quality
l30_bands = 10 cloudcover
= []
ndwi_band_links
for i in item_collection:
if i.properties['eo:cloud_cover'] <= cloudcover:
if i.collection_id == 'HLSS30.v2.0':
#print(i.properties['eo:cloud_cover'])
= s30_bands
ndwi_bands elif i.collection_id == 'HLSL30.v2.0':
#print(i.properties['eo:cloud_cover'])
= l30_bands
ndwi_bands
for a in i.assets:
if any(b==a for b in ndwi_bands):
ndwi_band_links.append(i.assets[a].href)
10] ndwi_band_links[:
= defaultdict(list) tile_dicts
for l in ndwi_band_links:
= l.split('.')[-6]
tile tile_dicts[tile].append(l)
tile_dicts.keys()
= tile_dicts['T10SFJ'] tile_links
= defaultdict(list)
bands_dicts for b in tile_links:
= b.split('.')[-2]
band
bands_dicts[band].append(b)for i in bands_dicts:
print(i)
Locate Images in Amazon S3 Storage
= defaultdict(list)
path_dicts for l in bands_dicts['B05']:
= l.replace('https://data.lpdaac.earthdatacloud.nasa.gov/', 's3://')
s3l 'B05'].append(s3l)
path_dicts[
= []
s3paths_LB3 for l in bands_dicts['B03']:
= l.replace('https://data.lpdaac.earthdatacloud.nasa.gov/', 's3://')
s3l if s3l[38:39] == 'L':
'B03'].append(s3l) path_dicts[
= 'https://data.lpdaac.earthdatacloud.nasa.gov/s3credentials'
s3_cred_endpoint def get_temp_creds():
= s3_cred_endpoint
temp_creds_url return requests.get(temp_creds_url).json()
= get_temp_creds()
temp_creds_req = boto3.Session(aws_access_key_id=temp_creds_req['accessKeyId'],
session =temp_creds_req['secretAccessKey'],
aws_secret_access_key=temp_creds_req['sessionToken'],
aws_session_token='us-west-2') region_name
= rio.Env(AWSSession(session),
rio_env ='EMPTY_DIR',
GDAL_DISABLE_READDIR_ON_OPEN=os.path.expanduser('~/cookies.txt'),
GDAL_HTTP_COOKIEFILE=os.path.expanduser('~/cookies.txt'))
GDAL_HTTP_COOKIEJAR__enter__() rio_env.
Load images and visualize
def time_index_from_filenames(file_links):
return [datetime.strptime(f.split('.')[-5], '%Y%jT%H%M%S') for f in file_links]
= xr.Variable('time', time_index_from_filenames(path_dicts['B03']))
time =dict(band=1, x=512, y=512)
chunks= xr.concat([rioxarray.open_rasterio(f, chunks=chunks).squeeze('band', drop=True) for f in path_dicts['B03']], dim=time)
hls_ts_da_LB3 = xr.concat([rioxarray.open_rasterio(f, chunks=chunks).squeeze('band', drop=True) for f in path_dicts['B05']], dim=time)
hls_ts_da_LB5 = hls_ts_da_LB3.rio.reproject("epsg:4326")
hls_ts_da_LB3 = hls_ts_da_LB5.rio.reproject("epsg:4326") hls_ts_da_LB5
= hls_ts_da_LB3.load()
hls_ts_da_data_LB3 = hls_ts_da_LB5.load()
hls_ts_da_data_LB5 = hls_ts_da_data_LB3.rio.clip([roi])
hls_ts_da_data_LB3 = hls_ts_da_data_LB5.rio.clip([roi]) hls_ts_da_data_LB5
='x', y='y', rasterize=True, width=600, height=400, colorbar=True, cmap='gray').opts(clim=(0,2000)) hls_ts_da_data_LB5.hvplot.image(x
Caclulate Normalized Difference Water Index (NDWI) and Classify Innundated Areas
= hls_ts_da_data_LB3
LB3 = hls_ts_da_data_LB5
LB5 = (LB3-LB5)/(LB3+LB5)
NDWI ='x', y='y', rasterize=True, width=600, height=400, colorbar=True, cmap='coolwarm').opts(clim=(-0.5,0.5)) NDWI.hvplot.image(x
= NDWI>0
water ='x', y='y', rasterize=True, width=600, height=400, colorbar=True, cmap='PuOr').opts(clim=(0,1)) water.hvplot.image(x
Caclulate surface area of reservoir and plot time series
if water.variable.max() == True:
= water*30*30
water_real = water_real.sum(axis=(1,2))
water_area
%matplotlib inline
= plt.subplots()
fig, ax /1000000).plot(ax=ax, linewidth=2, linestyle = '-', marker='o')
(water_area[:]"Surface area of waterbody in km2")
ax.set_title('Area [km^2]') ax.set_ylabel(