4444import dask
4545import dask .array
4646import geopandas as gpd
47+ import hvplot .xarray
4748import numpy as np
4849import pandas as pd
4950import panel as pn
5051import pygmt
5152import scipy .spatial
5253import shapely .geometry
5354import tqdm
55+ import xarray as xr
5456import zarr
5557
5658import deepicedrain
@@ -339,15 +341,18 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series:
339341
340342
341343# %% [markdown]
342- # # Select a lake to examine
344+ # # Select a subglacial lake to examine
343345
344346# %%
345347# Save or load dhdt data from Parquet file
346- placename : str = "Recovery" # "Whillans"
347- drainage_basins : gpd .GeoDataFrame = drainage_basins .set_index (keys = "NAME" )
348- region : deepicedrain .Region = deepicedrain .Region .from_gdf (
349- gdf = drainage_basins .loc [placename ], name = "Recovery Basin"
350- )
348+ placename : str = "siple_coast" # "Recovery" # "Whillans"
349+ try :
350+ drainage_basins : gpd .GeoDataFrame = drainage_basins .set_index (keys = "NAME" )
351+ region : deepicedrain .Region = deepicedrain .Region .from_gdf (
352+ gdf = drainage_basins .loc [placename ], name = "Recovery Basin"
353+ )
354+ except KeyError :
355+ pass
351356df_dhdt : cudf .DataFrame = cudf .read_parquet (
352357 f"ATLXI/df_dhdt_{ placename .lower ()} .parquet"
353358)
@@ -356,29 +361,158 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series:
356361# %%
357362# Antarctic subglacial lake polygons with EPSG:3031 coordinates
358363antarctic_lakes : gpd .GeoDataFrame = gpd .read_file (
359- filename = "antarctic_subglacial_lakes .geojson"
364+ filename = "antarctic_subglacial_lakes_3031 .geojson"
360365)
361366
362367# %%
363368# Choose one draining/filling lake
364- draining : bool = False # False
365- placename : str = "Slessor" # "Whillans"
369+ draining : bool = False
370+ placename : str = "Whillans" # " Slessor" # "Kamb" # "Mercer" #
366371lakes : gpd .GeoDataFrame = antarctic_lakes .query (expr = "basin_name == @placename" )
367- lake = lakes .loc [lakes .maxabsdhdt .idxmin () if draining else lakes .maxabsdhdt .idxmax ()]
372+ lake = lakes .loc [lakes .inner_dhdt .idxmin () if draining else lakes .inner_dhdt .idxmax ()]
373+ # lake = lakes.query(expr="inner_dhdt < 0" if draining else "inner_dhdt > 0").loc[63]
368374lakedict = {
369- 76 : "Subglacial Lake Conway" , # draining lake
370- 78 : "Whillans IX" , # filling lake
371- 103 : "Slessor 45" , # draining lake
372- 108 : "Slessor 23" , # filling lake
375+ 21 : "Mercer 2b" , # filling lake
376+ 40 : "Subglacial Lake Conway" , # draining lake
377+ 48 : "Subglacial Lake Whillans" , # filling lake
378+ 50 : "Whillans IX" , # filling lake
379+ 63 : "Kamb 1" , # filling lake
380+ 65 : "Kamb 12" , # filling lake
381+ 97 : "MacAyeal 1" , # draining lake
382+ 109 : "Slessor 45" , # draining lake
383+ 116 : "Slessor 23" , # filling lake
384+ 153 : "Recovery IX" , # draining lake
385+ 157 : "Recovery 3" , # filling lake
373386}
374387region = deepicedrain .Region .from_gdf (gdf = lake , name = lakedict [lake .name ])
375388
389+ print (lake )
390+ lake .geometry
391+
376392# %%
377393# Subset data to lake of interest
378394placename : str = region .name .lower ().replace (" " , "_" )
379395df_lake : cudf .DataFrame = region .subset (data = df_dhdt )
380396
381397
398+ # %% [markdown]
399+ # ## Create an interpolated ice surface elevation grid for each ICESat-2 cycle
400+
401+ # %%
402+ # Generate gridded time-series of ice elevation over lake
403+ cycles : tuple = (3 , 4 , 5 , 6 , 7 , 8 )
404+ os .makedirs (name = f"figures/{ placename } " , exist_ok = True )
405+ ds_lake : xr .Dataset = deepicedrain .spatiotemporal_cube (
406+ table = df_lake .to_pandas (),
407+ placename = placename ,
408+ cycles = cycles ,
409+ folder = f"figures/{ placename } " ,
410+ )
411+ ds_lake .to_netcdf (path = f"figures/{ placename } /xyht_{ placename } .nc" , mode = "w" )
412+
413+ # %%
414+ # Get 3D grid_region (xmin/xmax/ymin/ymax/zmin/zmax),
415+ # and calculate normalized z-values as Elevation delta relative to Cycle 3
416+ grid_region = pygmt .info (table = df_lake [["x" , "y" ]].to_pandas (), spacing = "s250" )
417+ z_limits : tuple = (float (ds_lake .z .min ()), float (ds_lake .z .max ())) # original z limits
418+ grid_region : np .ndarray = np .append (arr = grid_region , values = z_limits )
419+
420+ ds_lake_norm : xr .Dataset = ds_lake - ds_lake .sel (cycle_number = 3 ).z
421+ z_norm_limits : tuple = (float (ds_lake_norm .z .min ()), float (ds_lake_norm .z .max ()))
422+ grid_region_norm : np .ndarray = np .append (arr = grid_region [:4 ], values = z_norm_limits )
423+
424+ print (f"Elevation limits are: { z_limits } " )
425+
426+ # %%
427+ # 3D plots of gridded ice surface elevation over time
428+ azimuth : float = 157.5 # 202.5 # 270
429+ elevation : float = 45 # 60
430+ for cycle in tqdm .tqdm (iterable = cycles ):
431+ time_nsec : pd .Timestamp = df_lake [f"utc_time_{ cycle } " ].to_pandas ().mean ()
432+ time_sec : str = np .datetime_as_string (arr = time_nsec .to_datetime64 (), unit = "s" )
433+
434+ grdview_kwargs = dict (
435+ cmap = True ,
436+ zscale = 0.1 , # zscaling factor, default to 10x vertical exaggeration
437+ surftype = "sim" , # surface, image and mesh plot
438+ perspective = [azimuth , elevation ], # perspective using azimuth/elevation
439+ # W="c0.05p,black,solid", # draw contours
440+ )
441+
442+ fig = pygmt .Figure ()
443+ # grid = ds_lake.sel(cycle_number=cycle).z
444+ grid = f"figures/{ placename } /h_corr_{ placename } _cycle_{ cycle } .nc"
445+ pygmt .makecpt (cmap = "lapaz" , series = z_limits )
446+ fig .grdview (
447+ grid = grid ,
448+ projection = "X10c" ,
449+ region = grid_region ,
450+ shading = True ,
451+ frame = [
452+ f'SWZ+t"{ region .name } "' , # plot South, West axes, and Z-axis
453+ 'xaf+l"Polar Stereographic X (m)"' , # add x-axis annotations and minor ticks
454+ 'yaf+l"Polar Stereographic Y (m)"' , # add y-axis annotations and minor ticks
455+ f'zaf+l"Elevation (m)"' , # add z-axis annotations, minor ticks and axis label
456+ ],
457+ ** grdview_kwargs ,
458+ )
459+
460+ # Plot lake boundary outline
461+ # TODO wait for plot3d to plot lake boundary points at correct height
462+ df = pd .DataFrame ([region .bounds ()]).values
463+ points = pd .DataFrame (
464+ data = [point for point in lake .geometry .exterior .coords ], columns = ("x" , "y" )
465+ )
466+ df_xyz = pygmt .grdtrack (points = points , grid = grid , newcolname = "z" )
467+ fig .plot (
468+ data = df_xyz .values ,
469+ region = grid_region ,
470+ pen = "1.5p,yellow2" ,
471+ Jz = True , # zscale
472+ p = f"{ azimuth } /{ elevation } /{ df_xyz .z .median ()} " , # perspective
473+ # label='"Subglacial Lake X"'
474+ )
475+
476+ # Plot normalized elevation change
477+ grid = ds_lake_norm .sel (cycle_number = cycle ).z
478+ if cycle == 3 :
479+ # add some tiny random noise to make plot work
480+ grid = grid + np .random .normal (scale = 1e-8 , size = grid .shape )
481+ pygmt .makecpt (cmap = "vik" , series = z_norm_limits )
482+ fig .grdview (
483+ grid = grid ,
484+ region = grid_region_norm ,
485+ frame = [
486+ f'SEZ2+t"Cycle { cycle } at { time_sec } "' , # plot South, East axes, and Z-axis
487+ 'xaf+l"Polar Stereographic X (m)"' , # add x-axis annotations and minor ticks
488+ 'yaf+l"Polar Stereographic Y (m)"' , # add y-axis annotations and minor ticks
489+ f'zaf+l"Elev Change (m)"' , # add z-axis annotations, minor ticks and axis label
490+ ],
491+ X = "10c" , # xshift
492+ ** grdview_kwargs ,
493+ )
494+
495+ fig .savefig (f"figures/{ placename } /dsm_{ placename } _cycle_{ cycle } .png" )
496+ fig .show ()
497+
498+ # %%
499+ # Make a animated GIF of changing ice surface from the PNG files
500+ gif_fname : str = (
501+ f"figures/{ placename } /dsm_{ placename } _cycles_{ cycles [0 ]} -{ cycles [- 1 ]} .gif"
502+ )
503+ # !convert -delay 120 -loop 0 figures/{placename}/dsm_*.png {gif_fname}
504+
505+ # %%
506+ # HvPlot 2D interactive view of ice surface elevation grids over each ICESat-2 cycle
507+ dashboard : pn .layout .Column = pn .Column (
508+ ds_lake .hvplot .image (x = "x" , y = "y" , clim = z_limits , cmap = "gist_earth" , data_aspect = 1 )
509+ # * ds_lake.hvplot.contour(x="x", y="y", clim=z_limits, data_aspect=1)
510+ )
511+ dashboard .show (port = 30227 )
512+
513+ # %% [markdown]
514+ # ## Along track plots of ice surface elevation change over time
515+
382516# %%
383517# Select a few Reference Ground tracks to look at
384518rgts : list = [int (rgt ) for rgt in lake .refgtracks .split ("|" )]
@@ -445,6 +579,10 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series:
445579# Parallelized paired crossover analysis
446580futures : list = []
447581for rgt1 , rgt2 in itertools .combinations (rgts , r = 2 ):
582+ # skip if same referencegroundtrack but different laser pair
583+ # as they are parallel and won't cross
584+ if rgt1 [:4 ] == rgt2 [:4 ]:
585+ continue
448586 track1 = track_dict [rgt1 ][["x" , "y" , "h_corr" , "utc_time" ]]
449587 track2 = track_dict [rgt2 ][["x" , "y" , "h_corr" , "utc_time" ]]
450588 future = client .submit (
@@ -511,14 +649,20 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series:
511649pygmt .makecpt (cmap = "batlow" , series = [sumstats [var ]["25%" ], sumstats [var ]["75%" ]])
512650# Map frame in metre units
513651fig .basemap (frame = "f" , region = plotregion , projection = "X8c" )
514- # Plot actual track points
652+ # Plot actual track points in green
515653for track in tracks :
516- fig .plot (x = track .x , y = track .y , color = "green" , style = "c0.01c" )
654+ tracklabel = f"{ track .iloc [0 ].referencegroundtrack } { track .iloc [0 ].pairtrack } "
655+ fig .plot (
656+ x = track .x ,
657+ y = track .y ,
658+ pen = "thinnest,green,." ,
659+ style = f'qN+1:+l"{ tracklabel } "+f3p,Helvetica,darkgreen' ,
660+ )
517661# Plot crossover point locations
518662fig .plot (x = df .x , y = df .y , color = df .h_X , cmap = True , style = "c0.1c" , pen = "thinnest" )
519- # PLot lake boundary
663+ # Plot lake boundary in blue
520664lakex , lakey = lake .geometry .exterior .coords .xy
521- fig .plot (x = lakex , y = lakey , pen = "thin" )
665+ fig .plot (x = lakex , y = lakey , pen = "thin,blue,-. " )
522666# Map frame in kilometre units
523667fig .basemap (
524668 frame = [
@@ -530,7 +674,7 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series:
530674 projection = "X8c" ,
531675)
532676fig .colorbar (position = "JMR" , frame = ['x+l"Crossover Error"' , "y+lm" ])
533- fig .savefig (f"figures/crossover_area_{ placename } .png" )
677+ fig .savefig (f"figures/{ placename } / crossover_area_{ placename } _ { min_date } _ { max_date } .png" )
534678fig .show ()
535679
536680
@@ -587,14 +731,14 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series:
587731# Plot dashed line connecting points
588732fig .plot (x = df_max .t , y = df_max .h , pen = f"faint,blue,-" )
589733fig .savefig (
590- f"figures/crossover_point_{ placename } _{ track1 } _{ track2 } _{ min_date } _{ max_date } .png"
734+ f"figures/{ placename } / crossover_point_{ placename } _{ track1 } _{ track2 } _{ min_date } _{ max_date } .png"
591735)
592736fig .show ()
593737
594738# %%
595739# Plot all crossover height points over time over the lake area
596- fig = deepicedrain .vizplots . plot_crossovers (df = df_th , regionname = region .name )
597- fig .savefig (f"figures/crossover_many_{ placename } _{ min_date } _{ max_date } .png" )
740+ fig = deepicedrain .plot_crossovers (df = df_th , regionname = region .name )
741+ fig .savefig (f"figures/{ placename } / crossover_many_{ placename } _{ min_date } _{ max_date } .png" )
598742fig .show ()
599743
600744# %%
@@ -603,10 +747,15 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series:
603747normfunc = lambda h : h - h .iloc [0 ] # lambda h: h - h.mean()
604748df_th ["h_norm" ] = df_th .groupby (by = "track1_track2" ).h .transform (func = normfunc )
605749
606- fig = deepicedrain .vizplots .plot_crossovers (
607- df = df_th , regionname = region .name , elev_var = "h_norm"
750+ fig = deepicedrain .plot_crossovers (
751+ df = df_th ,
752+ regionname = region .name ,
753+ elev_var = "h_norm" ,
754+ elev_filter = 3 * abs (df .h_X ).median (),
755+ )
756+ fig .savefig (
757+ f"figures/{ placename } /crossover_many_normalized_{ placename } _{ min_date } _{ max_date } .png"
608758)
609- fig .savefig (f"figures/crossover_many_normalized_{ placename } _{ min_date } _{ max_date } .png" )
610759fig .show ()
611760
612761# %%
0 commit comments