Polyedit

Download this notebook from GitHub (right-click to download).


Title: PolyEdit

Description: A linked streams example demonstrating how to use the PolyEdit stream.

Dependencies: Bokeh

Backends: Bokeh

In [1]:
import numpy as np
import holoviews as hv
from holoviews import opts, streams

hv.extension('bokeh')

The PolyEdit stream adds a bokeh tool to the source plot, which allows drawing, dragging and deleting vertices on polygons and making the drawn data available to Python. The tool supports the following actions:

Show vertices

Double tap an existing patch or multi-line

Add vertex

Double tap an existing vertex to select it, the tool will draw the next point, to add it tap in a new location.
To finish editing and add a point double tap otherwise press the ESC key to cancel.

Move vertex

Drag an existing vertex and let go of the mouse button to release it.

Delete vertex

After selecting one or more vertices press BACKSPACE while the mouse cursor is within the plot area.

As a simple example we will draw a number of boxes and ellipses by displaying them using a Polygons element and then link that element to two PolyEdit streams. Enabling the shared option allows editing multiple Polygons/Paths with the same tool. You may also supply a vertex_style dictionary defining the visual attributes of the vertices once you double tapped a polygon:

In [2]:
np.random.seed(42)
polys = hv.Polygons([hv.Box(*i, spec=np.random.rand()/3)
                     for i in np.random.rand(10, 2)])
ovals = hv.Polygons([hv.Ellipse(*i, spec=np.random.rand()/3)
                     for i in np.random.rand(10, 2)])
poly_edit = streams.PolyEdit(source=polys, vertex_style={'color': 'red'}, shared=True)
poly_edit2 = streams.PolyEdit(source=ovals, shared=True)

(polys * ovals).opts(
    opts.Polygons(active_tools=['poly_edit'], fill_alpha=0.4, height=400, width=400))
Out[2]:

Whenever the data source is edited the data is synced with Python, both in the notebook and when deployed on the bokeh server. The data is made available as a dictionary of columns:

In [3]:
poly_edit.data
Out[3]:
{'xs': [array([0.27256464, 0.27256464, 0.4765156 , 0.4765156 , 0.27256464]),
  array([0.70874497, 0.70874497, 0.75524292, 0.75524292, 0.70874497]),
  array([0.10732787, 0.10732787, 0.20470942, 0.20470942, 0.10732787]),
  array([-0.0029767 , -0.0029767 ,  0.11914392,  0.11914392, -0.0029767 ]),
  array([0.52510335, 0.52510335, 0.67712668, 0.67712668, 0.52510335]),
  array([-0.11027817, -0.11027817,  0.15144715,  0.15144715, -0.11027817]),
  array([0.79916368, 0.79916368, 0.8657216 , 0.8657216 , 0.79916368]),
  array([0.09611923, 0.09611923, 0.26753071, 0.26753071, 0.09611923]),
  array([0.20550648, 0.20550648, 0.402978  , 0.402978  , 0.20550648]),
  array([0.42420328, 0.42420328, 0.43968675, 0.43968675, 0.42420328])],
 'ys': [array([0.84873882, 1.05268979, 1.05268979, 0.84873882, 0.84873882]),
  array([0.57540951, 0.62190746, 0.62190746, 0.57540951, 0.57540951]),
  array([0.10730375, 0.2046853 , 0.2046853 , 0.10730375, 0.10730375]),
  array([0.80511584, 0.92723645, 0.92723645, 0.80511584, 0.80511584]),
  array([0.63206091, 0.78408424, 0.78408424, 0.63206091, 0.63206091]),
  array([0.83904719, 1.10077251, 1.10077251, 0.83904719, 0.83904719]),
  array([0.17906015, 0.24561807, 0.24561807, 0.17906015, 0.17906015]),
  array([0.09769877, 0.26911025, 0.26911025, 0.09769877, 0.09769877]),
  array([0.42602067, 0.62349219, 0.62349219, 0.42602067, 0.42602067]),
  array([0.2834874 , 0.29897088, 0.29897088, 0.2834874 , 0.2834874 ])]}

Alternatively we can use the element property to get an Element containing the returned data:

In [4]:
poly_edit.element
Out[4]:

Download this notebook from GitHub (right-click to download).