import holoviews as hv
import numpy as np
import zarr
from holoviews import opts
from bokeh.models import WheelZoomToolPlot
Note that the plotting functions here make use of dynamic updates, which require a running Jupyter server. When viewed statically (as this documentation website), the plots will not update fully when you zoom.
ras_plot
ras_plot (data:str, post_proc:Callable=None, n_kdim:int=None, bounds:tuple=None, level_increase=0)
plot rendered stack of ras tiles.
| Type | Default | Details | |
|---|---|---|---|
| data | str | directory to the rendered ras pyramid | |
| post_proc | Callable | None | function for the post processing, can be None, ‘intf_0’, ‘intf_seq’, ‘intf_all’ or user-defined function |
| n_kdim | int | None | number of key dimensions, can only be 0 or 1 or 2, ndim of raster dataset -2 by default |
| bounds | tuple | None | bounding box (x0, y0, x_max, y_max) |
| level_increase | int | 0 | amount of zoom level increase for more clear point show and faster responds time |
Usage:
rslc_ = '../../data/rslc.zarr/'
rslc = zarr.open(rslc_,mode='r')[:]intf_plot = ras_plot(np.angle(rslc[...,2]*rslc[...,0].conj()))intf_plot = intf_plot.redim(x=hv.Dimension('r', label='Range'), y=hv.Dimension('az',label='Azimuth'), z=hv.Dimension('Phase',range=(-np.pi,np.pi)))intf_plot.opts(opts.Image(cmap='colorwheel',width=600, height=400, colorbar=True,
invert_yaxis=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],active_tools=['wheel_zoom']))ras_plot can also take stack of raster images. It will return DynamicMap with keys. Here we define a function to generate interferograms with the first SLC as reference:
def intf_0(data, xslice, yslice,i):
return np.angle(data[yslice,xslice,0]*data[yslice,xslice,i].conj())intf_plot = ras_plot(rslc,post_proc=intf_0, level_increase=0)We have a set of convenient predefined post_proc functions, e.g., intf_0, intf_seq, intf_all. The above code equals to:
intf_plot = ras_plot(rslc,post_proc='intf_0', level_increase=0)Add annotations:
dates = ["20210802", "20210816", "20210830", "20210913", "20211011", "20211025", "20220606", "20220620",
"20220704", "20220718", "20220801", "20220815", "20220829", "20220912", "20220926", "20221010",
"20221024",]
intf_plot = intf_plot.redim(i=hv.Dimension('i', label='Interferogram', range=(0,16), value_format=(lambda i: dates[0]+'_'+dates[i])),
x=hv.Dimension('r', label='Range'), y=hv.Dimension('az',label='Azimuth'), z=hv.Dimension('Phase',range=(-np.pi,np.pi)))Specify plotting options and plot:
hv.output(widget_location='bottom')
intf_plot.opts(opts.Image(cmap='colorwheel',width=600, height=400, colorbar=True,
invert_yaxis=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],active_tools=['wheel_zoom']))Or the intensity:
def intensity(data, xslice, yslice,i):
return np.log(np.abs(data[yslice,xslice,i])**2)
int_plot = ras_plot(rslc,post_proc=intensity)
int_plot = int_plot.redim(i=hv.Dimension('i', label='Intensity', range=(1,16), value_format=(lambda i: dates[i])),
x=hv.Dimension('r', label='Range'), y=hv.Dimension('az',label='Azimuth'), z=hv.Dimension('Intensity'))
int_plot.opts(opts.Image(cmap='gray',width=600, height=600, colorbar=True,
invert_yaxis=True, default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],
active_tools=['wheel_zoom']))We can also plot sequential interferograms. In this case, we only plot 26 interferograms.
def intf_seq(data, xslice, yslice,i):
return np.angle(data[yslice,xslice,i]*data[yslice,xslice,i+1].conj())
intf_plot = ras_plot(rslc,post_proc=intf_seq)
# or
intf_plot = ras_plot(rslc,post_proc='intf_seq')
intf_plot = intf_plot.redim(i=hv.Dimension('i', label='Interferogram', range=(0,15), value_format=(lambda i: dates[i]+'_'+dates[i+1])),
x=hv.Dimension('r', label='Range'), y=hv.Dimension('az',label='Azimuth'), z=hv.Dimension('Phase',range=(-np.pi,np.pi)))
intf_plot.opts(opts.Image(cmap='colorwheel',width=600, height=600, colorbar=True,
invert_yaxis=True, default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],
active_tools=['wheel_zoom']))The n_kdim don’t have to be data.ndim-2. Here is an example to show all interferograms.
def intf_all(data, xslice, yslice,i,j): # we have 2 kdims here
return np.angle(data[yslice,xslice,i]*data[yslice,xslice,j].conj())intf_plot = ras_plot(rslc,post_proc=intf_all,n_kdim=2,level_increase=0)
# or
intf_plot = ras_plot(rslc,post_proc='intf_all',n_kdim=2,level_increase=0)Add annotations:
intf_plot = intf_plot.redim(i=hv.Dimension('i', label='Reference Image', range=(0,16), value_format=(lambda i: dates[i])),
j=hv.Dimension('j', label='Secondary Image', range=(0,16), value_format=(lambda i: dates[i])),
x=hv.Dimension('r', label='Range'), y=hv.Dimension('az',label='Azimuth'), z=hv.Dimension('Phase',range=(-np.pi,np.pi)))Specify plotting options and plot:
hv.output(widget_location='bottom')
intf_plot.opts(opts.Image(cmap='colorwheel',frame_width=500, frame_height=600, colorbar=True,
invert_yaxis=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],active_tools=['wheel_zoom']))pc_plot
pc_plot (data:numpy.ndarray, y:numpy.ndarray, x:numpy.ndarray, ras_resolution:float=20, post_proc_pc:Callable=None, n_kdim:int=None, rtree=None, level_increase=0)
plot rendered point cloud pyramid on the fly
| Type | Default | Details | |
|---|---|---|---|
| data | ndarray | pc dataset | |
| y | ndarray | y coordinate | |
| x | ndarray | x coordinate | |
| ras_resolution | float | 20 | minimum resolution of rendered raster data in the pyramid |
| post_proc_pc | Callable | None | function for the post processing |
| n_kdim | int | None | number of key dimensions, can only be 0 or 1 or 2, ndim of point cloud dataset -1 by default |
| rtree | NoneType | None | rtree, if not provide, will be automatically generated but may slow the program |
| level_increase | int | 0 | amount of zoom level increase for more clear point show and faster responds time |
pc_plot take the rendered point cloud dataset as the input and return a Holoviews DynamicMap. When the zoom level is -1, it plot the the raw point cloud data. When the zoom level is 0 or over 0, it plot the rasterized images. Just as ras_plot, it accept post processing functions for both point cloud data and raster data to be plot. It is the user’s duty to esure the post processing fuctions coincide with each other. It also accept n_kdim to set number of kdims for returned DynamicMap.
Here is an example to plot the amplitude dispersion index:
ps_can_adi = zarr.open('../CLI/ps/ps_can_adi.zarr/',mode='r')[:]
ps_can_rslc = zarr.open('../CLI/ps/ps_can_rslc.zarr/',mode='r')[:]
ps_can_x = zarr.open('../CLI/ps/ps_can_e.zarr/',mode='r')[:]
ps_can_y = zarr.open('../CLI/ps/ps_can_n.zarr/',mode='r')[:]adi_ras_plot, adi_pc_plot = pc_plot(ps_can_adi,ps_can_y,ps_can_x,level_increase=1)
adi_plot = adi_ras_plot*adi_pc_plotAdd annotations:
adi_plot = adi_plot.redim(x=hv.Dimension('lon', label='Longitude'), y=hv.Dimension('lat',label='Latitude'),
z=hv.Dimension('adi',label='Amplitude Dispersion Index',range=(0,0.3))
)Specify plotting options and plot:
adi_plot.opts(opts.Image(cmap='fire',width=600, height=400, colorbar=True,
# invert_yaxis=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],
active_tools=['wheel_zoom']
),
opts.Points(color='adi', cmap='fire',width=600, height=400, colorbar=True,
# invert_yaxis=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],
active_tools=['wheel_zoom']
)
)Add the optical image as the background:
hv.element.tiles.EsriImagery()*adi_plotNote that, for displaying data over tiles, the data have to be projected to the Web Mercator Projection.
As ras_plot, pc_plot can also take stack of point cloud dataset. It will return DynamicMap with keys. Here we define a function to generate interferograms with the first SLC as reference:
def intf_0_pc(data,idx_array,i):
return np.angle(data[idx_array,0]*data[idx_array,i].conj())intf_ras_plot, intf_pc_plot = pc_plot(ps_can_rslc, ps_can_y, ps_can_x, post_proc_pc=intf_0_pc, level_increase=1)
# or
intf_ras_plot, intf_pc_plot = pc_plot(ps_can_rslc, ps_can_y, ps_can_x, post_proc_pc='intf_0', level_increase=1)
intf_plot = intf_ras_plot*intf_pc_plotAdd annotations:
dates = ["20210802", "20210816", "20210830", "20210913", "20211011", "20211025", "20220606", "20220620",
"20220704", "20220718", "20220801", "20220815", "20220829", "20220912", "20220926", "20221010",
"20221024",]
intf_plot = intf_plot.redim(i=hv.Dimension('i', label='Interferogram', range=(0,16), value_format=(lambda i: dates[0]+'_'+dates[i])),
x=hv.Dimension('lon', label='Longitude'), y=hv.Dimension('lat',label='Latitude'), z=hv.Dimension('Phase',range=(-np.pi,np.pi)))Specify plotting options and plot:
hv.output(widget_location='bottom')
intf_plot.opts(opts.Image(cmap='colorwheel',width=600, height=400, colorbar=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],
active_tools=['wheel_zoom']
),
opts.Points(color='Phase', cmap='colorwheel',width=600, height=400, colorbar=True,
# invert_yaxis=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],
active_tools=['wheel_zoom']
)
)The n_kdim don’t have to be data.ndim-1. Here is an example to show all interferograms.
def intf_pc(data, idx,i,j): # we have 2 kdims here
return np.angle(data[idx,i]*data[idx,j].conj())intf_ras_plot, intf_pc_plot = pc_plot(ps_can_rslc, ps_can_y, ps_can_x, post_proc_pc=intf_pc,n_kdim=2,level_increase=0)
# or
intf_ras_plot, intf_pc_plot = pc_plot(ps_can_rslc, ps_can_y, ps_can_x, post_proc_pc='intf_all',n_kdim=2,level_increase=0)
intf_plot = intf_ras_plot*intf_pc_plotAdd annotations:
intf_plot = intf_plot.redim(i=hv.Dimension('i', label='Reference Image', range=(0,16), value_format=(lambda i: dates[i])),
j=hv.Dimension('j', label='Secondary Image', range=(0,16), value_format=(lambda i: dates[i])),
x=hv.Dimension('r', label='Range'), y=hv.Dimension('az',label='Azimuth'), z=hv.Dimension('Phase',range=(-np.pi,np.pi)))Specify plotting options and plot:
hv.output(widget_location='bottom')
intf_plot.opts(opts.Image(cmap='colorwheel',width=600, height=400, colorbar=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover','tap'],
active_tools=['wheel_zoom']
),
opts.Points(color='Phase', cmap='colorwheel',width=600, height=400, colorbar=True,
# invert_yaxis=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover','tap'],
active_tools=['wheel_zoom']
)
)ts_plot
ts_plot (v:numpy.ndarray, t, source_plot, kdtree=None, y:numpy.ndarray=None, x:numpy.ndarray=None, reference=False, post_proc_ts=None)
Plot time series with clicked point.
| Type | Default | Details | |
|---|---|---|---|
| v | ndarray | pc dataset | |
| t | temporal coordinate | ||
| source_plot | source plot | ||
| kdtree | NoneType | None | kdtree, optional |
| y | ndarray | None | y coordinate, must be provided if kdtree is not provided |
| x | ndarray | None | x coordinate, must be provided if kdtree is not provided |
| reference | bool | False | allow set reference points (double click) |
| post_proc_ts | NoneType | None |
Usage:
dates_iso = [d[:4] + '-' + d[4:6] + '-' + d[6:] for d in dates]
dates_obj = np.array(dates_iso, dtype='datetime64[D]')data = np.tile(np.arange(ps_can_rslc.shape[0])[:,None],(1, ps_can_rslc.shape[1]))phase_ts_plot = ts_plot(data, dates_obj, intf_pc_plot , y=ps_can_y, x=ps_can_x, reference=True)phase_ts_plot = phase_ts_plot.redim(
t = hv.Dimension('t',label='Dates'),
v = hv.Dimension('v',label='Phase difference'),
)(phase_ts_plot.opts(
opts.Scatter(
tools=['hover'],
frame_width=500, #ylim=(-np.pi, np.pi),
size=10,
)
) +\
intf_plot.opts(opts.Image(cmap='colorwheel',width=600, height=400, colorbar=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],
active_tools=['wheel_zoom']
),
opts.Points(color='Phase', cmap='colorwheel',width=600, height=400, colorbar=True,
# invert_yaxis=True,
default_tools=['pan',WheelZoomTool(zoom_on_axis=False),'save','reset','hover'],
active_tools=['wheel_zoom']
)
)
).cols(1)