{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### **Title**: PointDraw Stream\n",
    "\n",
    "**Description**: A linked streams example demonstrating how to use the PointDraw stream.\n",
    "\n",
    "**Dependencies**: Bokeh\n",
    "    \n",
    "**Backends**: [Bokeh](./PointDraw.ipynb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import holoviews as hv\n",
    "from holoviews import opts, streams\n",
    "from holoviews.plotting.links import DataLink\n",
    "\n",
    "hv.extension('bokeh')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``PointDraw`` stream adds a bokeh tool to the source plot, which allows drawing, dragging and deleting points and making the drawn data available to Python. The tool supports the following actions:\n",
    "\n",
    "**Add point**\n",
    "\n",
    "    Tap anywhere on the plot\n",
    "\n",
    "**Move point**\n",
    "    \n",
    "    Tap and drag an existing point, the point will be dropped once you let go of the mouse button.\n",
    "\n",
    "**Delete point**\n",
    "\n",
    "    Tap a point to select it then press BACKSPACE or DELETE key while the mouse is within the plot area."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As a simple example we will create a ``PointDraw`` stream and attach it to a set of ``Points`` with a color dimension. By using a `DataLink` we can additionally link the `Points` to a Table containing the same data.\n",
    "\n",
    "We can now add drag and delete points, see the x/y position change in the table and edit the a color for each point in the table. Additionally the ``empty_value`` parameter on the ``PointDraw`` stream lets us define the value that will be inserted on columns other than the x/y position, which we can use here to set new points to 'black'. Finally we can limit the number of points using the ``num_objects`` option, ensuring that once the limit is reached the oldest point is dropped."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = ([0, 0.5, 1], [0, 0.5, 0], ['red', 'green', 'blue'])\n",
    "points = hv.Points(data, vdims='color').redim.range(x=(-.1, 1.1), y=(-.1, 1.1))\n",
    "point_stream = streams.PointDraw(data=points.columns(), num_objects=10, source=points, empty_value='black')\n",
    "table = hv.Table(points, ['x', 'y'], 'color')\n",
    "DataLink(points, table)\n",
    "\n",
    "(points + table).opts(\n",
    "    opts.Layout(merge_tools=False),\n",
    "    opts.Points(active_tools=['point_draw'], color='color', height=400,\n",
    "                size=10, tools=['hover'], width=400),\n",
    "    opts.Table(editable=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center><img src=\"https://assets.holoviews.org/gifs/examples/streams/bokeh/point_draw.gif\" width=800></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "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:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "point_stream.data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively we can use the ``element`` property to get an Element containing the returned data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "point_stream.element"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
