Skip to content

Commit

Permalink
💫 3D view of interpolated ice surface elev over ICESat-2 cycles
Browse files Browse the repository at this point in the history
Visualizing ice surface changes in 3D, because it's more intuitive to the eye! There's a bit of an art to 3D perspective plots, and PyGMT's `grdview` does a fine job at it, though there's many lines of code involved! The figure consists of two plots. On the left is a shaded ice surface elevation grid overlaid with the subglacial lake outline in yellow. On the right is an elevation difference/anomaly grid view (relative to ICESat-2 Cycle 3).

Still need to wait for `subplot` and `plot3d` to be wrapped in PyGMT to simplify the code and make things look more polished. Animated GIF is made using imagemagick's `convert` in bash as it was higher quality (and less lines of code) than Pillow. Also have a code cell showing an alternative HvPlot-based 2D map of the same time-series grid, complete with an interactive slider to cycle through each ICESat-2 cycle.
  • Loading branch information
weiji14 committed Oct 26, 2020
1 parent a63fad2 commit 38237d1
Show file tree
Hide file tree
Showing 2 changed files with 331 additions and 7 deletions.
216 changes: 210 additions & 6 deletions atlxi_lake.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@
"import dask\n",
"import dask.array\n",
"import geopandas as gpd\n",
"import hvplot.xarray\n",
"import numpy as np\n",
"import pandas as pd\n",
"import panel as pn\n",
"import pygmt\n",
"import scipy.spatial\n",
"import shapely.geometry\n",
"import tqdm\n",
"import xarray as xr\n",
"import zarr\n",
"\n",
"import deepicedrain"
Expand Down Expand Up @@ -905,12 +907,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Select a lake to examine"
"# Select a subglacial lake to examine"
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 13,
"metadata": {
"lines_to_next_cell": 2
},
Expand All @@ -932,7 +934,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -944,11 +946,41 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 15,
"metadata": {
"lines_to_next_cell": 1
},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"basin_name Whillans\n",
"num_points 3276\n",
"outer_dhdt 0.377611\n",
"outer_std 1.42712\n",
"outer_mad 0.083278\n",
"inner_dhdt 2.12607\n",
"maxabsdhdt 8.65662\n",
"refgtracks 74|135|196|266|327|388|516|577|638|769|830|101...\n",
"geometry POLYGON ((-444681.0351994165 -545210.454471163...\n",
"Name: 50, dtype: object\n"
]
},
{
"data": {
"image/svg+xml": [
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"300\" height=\"300\" viewBox=\"-456991.1908163784 -545964.7144366237 20365.019067435118 18110.406576156733\" preserveAspectRatio=\"xMinYMin meet\"><g transform=\"matrix(1,0,0,-1,0,-1073819.0222970909)\"><path fill-rule=\"evenodd\" fill=\"#66cc99\" stroke=\"#555555\" stroke-width=\"135.76679378290078\" opacity=\"0.6\" d=\"M -444681.0351994165,-545210.4544711632 L -445059.4273992341,-545139.2030781403 L -447613.01038991945,-544185.9610993187 L -448604.6076458619,-543785.8544671827 L -448890.77904354926,-543669.5833568621 L -451986.0162835539,-542018.9649696343 L -454210.39452298917,-540418.8503920811 L -454408.2814011552,-540246.0395154255 L -455385.85207539203,-539162.6159726225 L -456224.89981433837,-537787.3535748171 L -456236.9308509178,-537500.1188677208 L -454509.0218480694,-532882.2389798661 L -454459.2975782437,-532851.9066176108 L -448142.3565408698,-529956.985603917 L -441807.6895563269,-528608.5678259275 L -441356.8655731373,-528717.5355566369 L -439391.13700329117,-530087.2909355733 L -438887.0564588413,-530681.5730842056 L -438879.5685567435,-530699.8250218468 L -437919.08106515603,-534731.9868097329 L -437380.43171440385,-542757.777147059 L -437400.2001665644,-542812.5670977917 L -437460.8738999268,-542976.4327690548 L -437481.0986066531,-543031.0538226315 L -438289.55547334306,-544026.4356033215 L -438338.1695958391,-544058.5196375257 L -444681.0351994165,-545210.4544711632 z\" /></g></svg>"
],
"text/plain": [
"<shapely.geometry.polygon.Polygon at 0x7f2190a02c10>"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Choose one draining/filling lake\n",
"draining: bool = False\n",
Expand Down Expand Up @@ -977,7 +1009,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 16,
"metadata": {
"lines_to_next_cell": 2
},
Expand All @@ -988,6 +1020,178 @@
"df_lake: cudf.DataFrame = region.subset(data=df_dhdt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create an interpolated ice surface elevation grid for each ICESat-2 cycle"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 6/6 [00:03<00:00, 1.71it/s]\n"
]
}
],
"source": [
"# Generate gridded time-series of ice elevation over lake\n",
"cycles: tuple = (3, 4, 5, 6, 7, 8)\n",
"os.makedirs(name=f\"figures/{placename}\", exist_ok=True)\n",
"ds_lake: xr.Dataset = deepicedrain.spatiotemporal_cube(\n",
" table=df_lake.to_pandas(),\n",
" placename=placename,\n",
" cycles=cycles,\n",
" folder=f\"figures/{placename}\",\n",
")\n",
"ds_lake.to_netcdf(path=f\"figures/{placename}/xyht_{placename}.nc\", mode=\"w\")"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Elevation limits are: (273.2418518066406, 310.9927062988281)\n"
]
}
],
"source": [
"# Get 3D grid_region (xmin/xmax/ymin/ymax/zmin/zmax),\n",
"# and calculate normalized z-values as Elevation delta relative to Cycle 3\n",
"grid_region = pygmt.info(table=df_lake[[\"x\", \"y\"]].to_pandas(), spacing=\"s250\")\n",
"z_limits: tuple = (float(ds_lake.z.min()), float(ds_lake.z.max())) # original z limits\n",
"grid_region: np.ndarray = np.append(arr=grid_region, values=z_limits)\n",
"\n",
"ds_lake_norm: xr.Dataset = ds_lake - ds_lake.sel(cycle_number=3).z\n",
"z_norm_limits: tuple = (float(ds_lake_norm.z.min()), float(ds_lake_norm.z.max()))\n",
"grid_region_norm: np.ndarray = np.append(arr=grid_region[:4], values=z_norm_limits)\n",
"\n",
"print(f\"Elevation limits are: {z_limits}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 3D plots of gridded ice surface elevation over time\n",
"azimuth: float = 157.5 # 202.5 # 270\n",
"elevation: float = 45 # 60\n",
"for cycle in tqdm.tqdm(iterable=cycles):\n",
" time_nsec: pd.Timestamp = df_lake[f\"utc_time_{cycle}\"].to_pandas().mean()\n",
" time_sec: str = np.datetime_as_string(arr=time_nsec.to_datetime64(), unit=\"s\")\n",
"\n",
" grdview_kwargs = dict(\n",
" cmap=True,\n",
" zscale=0.1, # zscaling factor, default to 10x vertical exaggeration\n",
" surftype=\"sim\", # surface, image and mesh plot\n",
" perspective=[azimuth, elevation], # perspective using azimuth/elevation\n",
" # W=\"c0.05p,black,solid\", # draw contours\n",
" )\n",
"\n",
" fig = pygmt.Figure()\n",
" # grid = ds_lake.sel(cycle_number=cycle).z\n",
" grid = f\"figures/{placename}/h_corr_{placename}_cycle_{cycle}.nc\"\n",
" pygmt.makecpt(cmap=\"lapaz\", series=z_limits)\n",
" fig.grdview(\n",
" grid=grid,\n",
" projection=\"X10c\",\n",
" region=grid_region,\n",
" shading=True,\n",
" frame=[\n",
" f'SWZ+t\"{region.name}\"', # plot South, West axes, and Z-axis\n",
" 'xaf+l\"Polar Stereographic X (m)\"', # add x-axis annotations and minor ticks\n",
" 'yaf+l\"Polar Stereographic Y (m)\"', # add y-axis annotations and minor ticks\n",
" f'zaf+l\"Elevation (m)\"', # add z-axis annotations, minor ticks and axis label\n",
" ],\n",
" **grdview_kwargs,\n",
" )\n",
"\n",
" # Plot lake boundary outline\n",
" # TODO wait for plot3d to plot lake boundary points at correct height\n",
" df = pd.DataFrame([region.bounds()]).values\n",
" points = pd.DataFrame(\n",
" data=[point for point in lake.geometry.exterior.coords], columns=(\"x\", \"y\")\n",
" )\n",
" df_xyz = pygmt.grdtrack(points=points, grid=grid, newcolname=\"z\")\n",
" fig.plot(\n",
" data=df_xyz.values,\n",
" region=grid_region,\n",
" pen=\"1.5p,yellow2\",\n",
" Jz=True, # zscale\n",
" p=f\"{azimuth}/{elevation}/{df_xyz.z.median()}\", # perspective\n",
" # label='\"Subglacial Lake X\"'\n",
" )\n",
"\n",
" # Plot normalized elevation change\n",
" grid = ds_lake_norm.sel(cycle_number=cycle).z\n",
" if cycle == 3:\n",
" # add some tiny random noise to make plot work\n",
" grid = grid + np.random.normal(scale=1e-8, size=grid.shape)\n",
" pygmt.makecpt(cmap=\"vik\", series=z_norm_limits)\n",
" fig.grdview(\n",
" grid=grid,\n",
" region=grid_region_norm,\n",
" frame=[\n",
" f'SEZ2+t\"Cycle {cycle} at {time_sec}\"', # plot South, East axes, and Z-axis\n",
" 'xaf+l\"Polar Stereographic X (m)\"', # add x-axis annotations and minor ticks\n",
" 'yaf+l\"Polar Stereographic Y (m)\"', # add y-axis annotations and minor ticks\n",
" f'zaf+l\"Elev Change (m)\"', # add z-axis annotations, minor ticks and axis label\n",
" ],\n",
" X=\"10c\", # xshift\n",
" **grdview_kwargs,\n",
" )\n",
"\n",
" fig.savefig(f\"figures/{placename}/dsm_{placename}_cycle_{cycle}.png\")\n",
"fig.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Make a animated GIF of changing ice surface from the PNG files\n",
"gif_fname: str = (\n",
" f\"figures/{placename}/dsm_{placename}_cycles_{cycles[0]}-{cycles[-1]}.gif\"\n",
")\n",
"# !convert -delay 120 -loop 0 figures/{placename}/dsm_*.png {gif_fname}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# HvPlot 2D interactive view of ice surface elevation grids over each ICESat-2 cycle\n",
"dashboard: pn.layout.Column = pn.Column(\n",
" ds_lake.hvplot.image(x=\"x\", y=\"y\", clim=z_limits, cmap=\"gist_earth\", data_aspect=1)\n",
" # * ds_lake.hvplot.contour(x=\"x\", y=\"y\", clim=z_limits, data_aspect=1)\n",
")\n",
"dashboard.show(port=30227)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Along track plots of ice surface elevation change over time"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down
Loading

0 comments on commit 38237d1

Please sign in to comment.