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
import time
'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 – Local Machine 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)
Download identified images to local computer
"downloads", exist_ok=True) os.makedirs(
def download(url: str, fname: str):
= requests.get(url, stream=True)
resp = int(resp.headers.get('content-length', 0))
total with open(fname, 'wb') as file, tqdm(
=fname,
desc=110,
ncols=total,
total='iB',
unit=True,
unit_scale=1024,
unit_divisoras bar:
) for data in resp.iter_content(chunk_size=1024):
= file.write(data)
size bar.update(size)
= defaultdict(list)
path_dicts print('Begin Downloading Imagery')
= time.time()
start_time for key in bands_dicts:
= bands_dicts[key]
url for u in url:
= u.split('/')[-1]
filename = './downloads/' + filename
path
download(u,path)
path_dicts[key].append(path)print('Download Complete')
print("--- %s seconds ---" % (time.time() - start_time))
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(