Skip to content

Instantly share code, notes, and snippets.

@DancingQuanta
Last active January 30, 2019 14:15
Show Gist options
  • Save DancingQuanta/8ffb09971b0e80bcd8ed1690c1ea72b4 to your computer and use it in GitHub Desktop.
Save DancingQuanta/8ffb09971b0e80bcd8ed1690c1ea72b4 to your computer and use it in GitHub Desktop.
Pyviz app for drawing points on an image
import xarray as xr
import numpy as np
import holoviews as hv
from holoviews.streams import PointDraw
from holoviews.plotting.links import DataLink
import panel as pn
import param
hv.extension('bokeh')
class DataLoader(param.Parameterized):
data = param.Parameter()
poi_coords = param.Dict({'x': [], 'y': []})
generate_data = param.Action(
default=lambda self: self._generate_data(),
precedence=1,
doc="""Button to generate data.""")
def __init__(self, **params):
super().__init__(**params)
self._layout = pn.Column(
pn.Param(self, parameters=['generate_data'], show_name=False),
#pn.Spacer(height=200, width=200)
)
def _generate_data(self):
# Random data
x = np.arange(0, 10)
y = x
z = x
data = np.random.rand(len(x), len(y), len(z))
self.data = xr.DataArray(data,
coords={'x': x, 'y': y, 'wavenumber': z},
dims=['x', 'y', 'wavenumber'],
name='Signal')
def display_xarray(self):
msg = """```
{}
```
""".format(repr(self.data))
self._layout.append(pn.pane.Markdown(msg))
def panel(self):
return self._layout
class DataViewer(param.Parameterized):
data = param.Parameter()
poi_coords = param.Dict({'x': [], 'y': []})
def point_draw_spatial_map(self, data):
# TODO: there will be a simpler way coming soon with last
# attribute soon to be added to holoviews
key = (self._spatial_map.callback.args
or tuple(d.values[0] for d in self._spatial_map.kdims))
img = self._spatial_map[key]
closest = img.closest(list(zip(data['x'], data['y'])))
return hv.Points(closest)
def label_point(self, points):
# TODO: automatically calculate relative offsets
# TODO: React to changes in table
# Label points
text = np.arange(len(points))
return hv.Labels(points.add_dimension(
'text', 0, text, vdim=True)).options(
xoffset=2, yoffset=2, fontsize=14, text_color='white')
def view_spatial_map(self):
# Setup image visualisation
# Plot image
self._spatial_map = hv.Dataset(self.data).to(hv.Image, ['x', 'y'], dynamic=True)
# Create a PointDraw stream for POI
self._poi_stream = PointDraw(data=self.poi_coords)
# Create a DynamicMap linked to a PointDraw stream using
# a function that snap points to center of a pixel
self._poi_points = hv.DynamicMap(self.point_draw_spatial_map,
streams=[self._poi_stream])
# Initialise label of points
point_labels = self._poi_points.map(self.label_point,
hv.Points,
link_inputs=False)
# Link labels to points
DataLink(self._poi_points, point_labels)
# Compose plot
plot = self._spatial_map * self._poi_points * point_labels
return pn.panel(plot)
def view_poi_table(self):
# Create a table
self._poi_table = hv.Table(
self._poi_stream.data, ['x', 'y']
).opts(height=500, width=200, editable=True)
# Link the data in points to table
DataLink(self._poi_points, self._poi_table)
return pn.panel(self._poi_table)
def panel(self):
spatial_map = self.view_spatial_map()
poi_table = self.view_poi_table()
return pn.Row(
spatial_map,
poi_table,
)
stages = [
('Load Data', DataLoader),
('Browser', DataViewer),
]
pipeline = pn.pipeline.Pipeline(stages, debug=True)
# Change width of buttons
pipeline.layout[0][1]._widget_box.width = 100
pipeline.layout[0][2]._widget_box.width = 100
pipeline.layout.servable()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment