Single Track Demo¶
Process a single ATL03 granule using SlideRule's ATL06-SR algorithm and compare the results to the existing ATL06 data product.
What is demonstrated¶
- The
icesat2.atl06
API is used to perform a SlideRule processing request of a single ATL03 granule - The
h5.h5p
API is used to read existing ATL06 datasets - The
matplotlib
package is used to plot the elevation profile of all three tracks in the granule (with the first track overlaid with the expected profile) - The
geopandas
package is used to produce a plot representing the geolocation of the elevations produced by SlideRule.
Points of interest¶
Most use cases for SlideRule use the higher level icesat2.atl06p
API which works on a region of interest; but this notebook shows some of the capabilities of SlideRule for working on individual granules.
In [1]:
# Suppress Warnings
import warnings
warnings.filterwarnings("ignore")
In [2]:
import re
import posixpath
import shapely.geometry
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from sliderule import icesat2, io, sliderule, earthdata, h5
In [3]:
# Configure Session
icesat2.init("slideruleearth.io")
Find ATL03 Granules¶
In [4]:
# find granules for a spatial and temporal query
box_lon = [-105, -105, -100, -100, -105]
box_lat = [-75, -77.5, -77.5, -75, -75]
poly = io.to_region(box_lon, box_lat)
resources = earthdata.cmr(short_name='ATL03', polygon=poly, time_start='2018-10-19', time_end='2018-10-20')
granule = resources[0]
Execute SlideRule Algorithm¶
In [5]:
%%time
# regular expression operator for extracting information from files
rx = re.compile(r'(ATL\d{2})(-\d{2})?_(\d{4})(\d{2})(\d{2})(\d{2})'
r'(\d{2})(\d{2})_(\d{4})(\d{2})(\d{2})_(\d{3})_(\d{2})(.*?).h5$')
# extract parameters from ICESat-2 granule
PRD,HEM,YY,MM,DD,HH,MN,SS,TRK,CYCL,GRN,RL,VRS,AUX=rx.findall(granule).pop()
# Build ATL06 Request
parms = {
"poly":poly,
"cnf": 4,
"ats": 20.0,
"cnt": 10,
"len": 40.0,
"res": 20.0
}
# Request ATL06 Data
gdf = icesat2.atl06(parms, granule)
# Return DataFrame
print("Reference Ground Tracks: {} to {}".format(min(gdf["rgt"]), max(gdf["rgt"])))
print("Cycle: {} to {}".format(min(gdf["cycle"]), max(gdf["cycle"])))
print("Retrieved {} points from SlideRule".format(len(gdf["h_mean"])))
Reference Ground Tracks: 325 to 325 Cycle: 1 to 1 Retrieved 51999 points from SlideRule CPU times: user 2.32 s, sys: 60.8 ms, total: 2.38 s Wall time: 7.3 s
In [6]:
def s3_retrieve(granule, **kwargs):
# set default keyword arguments
kwargs.setdefault('lon_key','longitude')
kwargs.setdefault('lat_key','latitude')
kwargs.setdefault('index_key','time')
kwargs.setdefault('polygon',None)
# regular expression operator for extracting information from files
rx = re.compile(r'(ATL\d{2})(-\d{2})?_(\d{4})(\d{2})(\d{2})(\d{2})'
r'(\d{2})(\d{2})_(\d{4})(\d{2})(\d{2})_(\d{3})_(\d{2})(.*?).h5$')
# extract parameters from ICESat-2 granule
PRD,HEM,YY,MM,DD,HH,MN,SS,TRK,CYCL,GRN,RL,VRS,AUX=rx.findall(granule).pop()
# variables of interest
if (PRD == 'ATL06'):
segment_group = "land_ice_segments"
segment_key = 'segment_id'
vnames = ['segment_id','delta_time','latitude','longitude',
'h_li','h_li_sigma','atl06_quality_summary',
'fit_statistics/dh_fit_dx','fit_statistics/dh_fit_dy',
'fit_statistics/dh_fit_dx_sigma','fit_statistics/n_fit_photons',
'fit_statistics/h_expected_rms','fit_statistics/h_robust_sprd',
'fit_statistics/w_surface_window_final','fit_statistics/h_mean']
elif (PRD == 'ATL08'):
segment_group = "land_segments"
segment_key = 'segment_id_beg'
vnames = ['segment_id_beg','segment_id_end','delta_time',
'latitude','longitude','brightness_flag','layer_flag',
'msw_flag','night_flag','terrain_flg','urban_flag',
'segment_landcover','segment_snowcover','segment_watermask',
'terrain/h_te_best_fit','terrain/h_te_uncertainty',
'terrain/terrain_slope','terrain/n_te_photons',
'canopy/h_canopy','canopy/h_canopy_uncertainty',
'canopy/canopy_flag','canopy/n_ca_photons']
# for each valid beam within the HDF5 file
frames = []
gt = dict(gt1l=10,gt1r=20,gt2l=30,gt2r=40,gt3l=50,gt3r=60)
atlas_sdp_epoch = np.datetime64('2018-01-01T00:00:00')
kwds = dict(startrow=0,numrows=-1)
for gtx in ['gt1l','gt1r','gt2l','gt2r','gt3l','gt3r']:
geodatasets = [dict(dataset=f'{gtx}/{segment_group}/{v}',**kwds) for v in vnames]
try:
# get datasets from s3
hidatasets = h5.h5p(geodatasets, granule, "icesat2")
# copy to new "flattened" dictionary
data = {posixpath.basename(key):var for key,var in hidatasets.items()}
# Generate Time Column
delta_time = (data['delta_time']*1e9).astype('timedelta64[ns]')
data['time'] = gpd.pd.to_datetime(atlas_sdp_epoch + delta_time)
except:
pass
else:
# copy filename parameters
data['rgt'] = [int(TRK)]*len(data['delta_time'])
data['cycle'] = [int(CYCL)]*len(data['delta_time'])
data['gt'] = [gt[gtx]]*len(data['delta_time'])
# pandas dataframe from compiled dictionary
frames.append(gpd.pd.DataFrame.from_dict(data))
# concatenate pandas dataframe
try:
df = gpd.pd.concat(frames)
except:
return sliderule.emptyframe()
# convert to a GeoDataFrame
lon_key,lat_key = (kwargs['lon_key'],kwargs['lat_key'])
geometry = gpd.points_from_xy(df[lon_key], df[lat_key])
gdf = gpd.GeoDataFrame(df.drop(columns=[lon_key,lat_key]),
geometry=geometry,crs='EPSG:4326')
# intersect with geometry in projected reference system
if kwargs['polygon'] is not None:
gdf = gpd.overlay(gdf.to_crs(kwargs['polygon'].crs),
kwargs['polygon'], how='intersection')
# sort values for reproducible output despite async processing
gdf.set_index(kwargs['index_key'], inplace=True)
gdf.sort_index(inplace=True)
# remove duplicate points
gdf = gdf[~gdf.index.duplicated()]
# convert back to original coordinate reference system
return gdf.to_crs('EPSG:4326')
In [7]:
# get standard ATL06 products
atl06_granule = f'ATL06_{YY}{MM}{DD}{HH}{MN}{SS}_{TRK}{CYCL}{GRN}_{RL}_{VRS}{AUX}.h5'
region_gs = gpd.GeoSeries(shapely.geometry.Polygon(np.c_[box_lon,box_lat]), crs='EPSG:4326')
region_gdf = gpd.GeoDataFrame(geometry=region_gs).to_crs('EPSG:3857')
atl06 = s3_retrieve(atl06_granule, polygon=region_gdf)
Compare Sliderule and ASAS Results¶
In [8]:
# Create Elevation Plot
fig,ax = plt.subplots(num=1, ncols=6, sharey=True, figsize=(12, 6))
locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
formatter = mdates.ConciseDateFormatter(locator)
# Plot Elevations for each track
tracks = dict(gt1l=10,gt1r=20,gt2l=30,gt2r=40,gt3l=50,gt3r=60)
for s,gt in enumerate(tracks.keys()):
sr = gdf[gdf["gt"] == tracks[gt]]
asas = atl06[(atl06["gt"] == tracks[gt]) &
(atl06["h_mean"] < 1e38) &
(atl06["segment_id"] >= sr["segment_id"][0]) &
(atl06["segment_id"] <= sr["segment_id"][-1])]
ax[s].set_title(gt)
ax[s].plot(sr.index.values, sr["h_mean"].values, zorder=1,
linewidth=1.0, color='mediumseagreen', label='SlideRule')
ax[s].plot(asas.index.values, asas["h_mean"].values, zorder=0,
linewidth=1.0, color='darkorchid', label='ASAS')
ax[s].xaxis.set_major_locator(locator)
ax[s].xaxis.set_major_formatter(formatter)
# add labels and legends
ax[0].set_ylabel('Height Above WGS84 Ellipsoid')
lgd = ax[0].legend(loc=3,frameon=False)
lgd.get_frame().set_alpha(1.0)
for line in lgd.get_lines():
line.set_linewidth(6)
# Show Plot
plt.show()
Map Plot¶
In [9]:
# Create PlateCarree Plot
fig,ax1 = plt.subplots(num=None, figsize=(12, 6))
################################
# add global plot
################################
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world.plot(ax=ax1, color='0.8', edgecolor='black')
gdf.plot(ax=ax1, marker='o', color='red', markersize=2.5, zorder=3)
ax1.set_title("SlideRule Global Reference")
# Plot locations of each track
tracks = dict(gt1l=10,gt1r=20,gt2l=30,gt2r=40,gt3l=50,gt3r=60)
for s,gt in enumerate(tracks.keys()):
sr = gdf[gdf["gt"] == tracks[gt]]
sr.plot(ax=ax1, marker='o', color='red', markersize=2.5, zorder=3)
# Plot Bounding Box
ax1.plot(box_lon, box_lat, linewidth=1.5, color='blue', zorder=2)
# x and y limits, axis = equal
ax1.set_xlim(-180,180)
ax1.set_ylim(-90,90)
ax1.set_aspect('equal', adjustable='box')
# show plot
plt.show()
In [ ]: