<NME> jupyter-notebook-demo.ipynb
<BEF> ADDFILE
<MSG> docs(python): add Jupyter notebook example

<DFF> @@ -0,0 +1,446 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import neuroglancer\n",
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Create a new (initially empty) viewer.  This starts a webserver in a background thread, which serves a copy of the Neuroglancer client, and which also can serve local volume data and handles sending and receiving Neuroglancer state updates."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "viewer = neuroglancer.Viewer()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Print a link to the viewer (only valid while the notebook kernel is running). Note that while the Viewer is running, anyone with the link can obtain any authentication credentials that the neuroglancer Python module obtains. Therefore, be very careful about sharing the link, and keep in mind that sharing the notebook will likely also share viewer links."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<a href=\"http://127.0.0.1:35105/v/eaca581808a5af91ecece9726db6929504ed6bf4/\" target=\"_blank\">Viewer</a>"
+      ],
+      "text/plain": [
+       "http://127.0.0.1:35105/v/eaca581808a5af91ecece9726db6929504ed6bf4/"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "viewer"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Add some example layers using the precomputed data source (HHMI Janelia FlyEM FIB-25 dataset)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "with viewer.txn() as s:\n",
+    "  s.layers['image'] = neuroglancer.ImageLayer(source='precomputed://gs://neuroglancer-public-data/flyem_fib-25/image')\n",
+    "  s.layers['segmentation'] = neuroglancer.SegmentationLayer(source='precomputed://gs://neuroglancer-public-data/flyem_fib-25/ground_truth')\n",
+    "  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Display a numpy array as an additional layer.  A reference to the numpy array is kept only as long as the layer remains in the viewer."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "a = np.zeros((3, 100, 100, 100), dtype=np.uint8)\n",
+    "ix, iy, iz = np.meshgrid(* [np.linspace(0, 1, n) for n in a.shape[1:]], indexing='ij')\n",
+    "a[0, :, :, :] = np.abs(np.sin(4 * (ix + iy))) * 255\n",
+    "a[1, :, :, :] = np.abs(np.sin(4 * (iy + iz))) * 255\n",
+    "a[2, :, :, :] = np.abs(np.sin(4 * (ix + iz))) * 255\n",
+    "\n",
+    "# This volume handle can be used to notify the viewer that the data has changed.\n",
+    "volume = neuroglancer.LocalVolume(a, voxel_size=[8, 8, 8], voxel_offset=[3000, 3000, 3000])\n",
+    "with viewer.txn() as s:\n",
+    "  s.layers['overlay'] = neuroglancer.ImageLayer(\n",
+    "        source=volume,\n",
+    "      # Define a custom shader to display this 3-channel array as RGB.\n",
+    "        shader=\"\"\"\n",
+    "void main() {\n",
+    "  emitRGB(vec3(toNormalized(getDataValue(0)),\n",
+    "               toNormalized(getDataValue(1)),\n",
+    "               toNormalized(getDataValue(2))));\n",
+    "}\n",
+    "\"\"\",\n",
+    "    )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Move the viewer position to where we added the overlay."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "with viewer.txn() as s:\n",
+    "    s.voxel_coordinates = [3000, 3000, 3000]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Modify the overlay volume, and call `invalidate()` to notify the Neuroglancer client."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "a[2, ...] = 0\n",
+    "volume.invalidate()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Select a couple segments."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "with viewer.txn() as s:\n",
+    "    s.layers['segmentation'].segments.update([1752, 88847])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Print the neuroglancer viewer state.  The Neuroglancer Python library provides a set of Python objects that wrap the JSON-encoded viewer state.  `viewer.state` returns a read-only snapshot of the state.  To modify the state, use the `viewer.txn()` function, or `viewer.set_state`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "ViewerState({\"layers\": {\"image\": {\"source\": \"precomputed://gs://neuroglancer-public-data/flyem_fib-25/image\", \"type\": \"image\"}, \"segmentation\": {\"source\": \"precomputed://gs://neuroglancer-public-data/flyem_fib-25/ground_truth\", \"type\": \"segmentation\", \"segments\": [\"1752\", \"88847\"]}, \"overlay\": {\"source\": \"python://eaca581808a5af91ecece9726db6929504ed6bf4.7484f279069e5e4611479e3700a2e97932a18d46\", \"type\": \"image\", \"shader\": \"\\nvoid main() {\\n  emitRGB(vec3(toNormalized(getDataValue(0)),\\n               toNormalized(getDataValue(1)),\\n               toNormalized(getDataValue(2))));\\n}\\n\"}}, \"navigation\": {\"pose\": {\"position\": {\"voxelSize\": [8, 8, 8], \"voxelCoordinates\": [3000, 3000, 3000]}}, \"zoomFactor\": 8}})"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "viewer.state"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Print the set of selected segments.|"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "frozenset({1752, 88847})"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "viewer.state.layers['segmentation'].segments"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Update the state by calling `set_state` directly."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "u'3a3ccf4a8160914a8fbae820a54e6ea73d9873b5'"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "import copy\n",
+    "new_state = copy.deepcopy(viewer.state)\n",
+    "new_state.layers['segmentation'].segments.add(10625)\n",
+    "viewer.set_state(new_state)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Bind the 't' key in neuroglancer to a Python action."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Got my-action\n",
+      "  Mouse position: [ 2892.  3084.  3000.]\n",
+      "  Layer selected values: Map({\"image\": 86, \"segmentation\": {\"t\": \"u64\", \"v\": \"1752\"}, \"overlay\": null})\n"
+     ]
+    }
+   ],
+   "source": [
+    "num_actions = 0\n",
+    "def my_action(s):\n",
+    "    global num_actions\n",
+    "    num_actions += 1\n",
+    "    with viewer.config_state.txn() as st:\n",
+    "      st.status_messages['hello'] = ('Got action %d: mouse position = %r' %\n",
+    "                                     (num_actions, s.mouse_voxel_coordinates))\n",
+    "    print('Got my-action')\n",
+    "    print('  Mouse position: %s' % (s.mouse_voxel_coordinates,))\n",
+    "    print('  Layer selected values: %s' % (s.selected_values,))\n",
+    "viewer.actions.add('my-action', my_action)\n",
+    "with viewer.config_state.txn() as s:\n",
+    "    s.input_event_bindings.viewer['keyt'] = 'my-action'\n",
+    "    s.status_messages['hello'] = 'Welcome to this example'"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Change the view layout to 3-d."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "with viewer.txn() as s:\n",
+    "    s.layout = '3d'\n",
+    "    s.perspective_zoom = 300"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Take a screenshot (useful for creating publication figures, or for generating videos).  While capturing the screenshot, we hide the UI and specify the viewer size so that we get a result independent of the browser size."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "application/vnd.jupyter.widget-view+json": {
+       "model_id": "0f86104c8fe449f68c167d0f75f39041",
+       "version_major": 2,
+       "version_minor": 0
+      },
+      "text/plain": [
+       "SW1hZ2UodmFsdWU9J1x4ODlQTkdcclxuXHgxYVxuXHgwMFx4MDBceDAwXHJJSERSXHgwMFx4MDBceDAzXHhiOFx4MDBceDAwXHgwM1x4ODRceDA4XHgwNlx4MDBceDAwXHgwMFx4OGJceDgxXHjigKY=\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "with viewer.config_state.txn() as s:\n",
+    "    s.show_ui_controls = False\n",
+    "    s.show_panel_borders = False\n",
+    "    s.viewer_size = [1000, 1000]\n",
+    "from ipywidgets import Image\n",
+    "screenshot_image = Image(value=viewer.screenshot().screenshot.image)\n",
+    "with viewer.config_state.txn() as s:\n",
+    "    s.show_ui_controls = True\n",
+    "    s.show_panel_borders = True\n",
+    "    s.viewer_size = None\n",
+    "screenshot_image"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Change the view layout to show the segmentation side by side with the image, rather than overlayed.  This can also be done from the UI by dragging and dropping.  The side by side views by default have synchronized position, orientation, and zoom level, but this can be changed."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "with viewer.txn() as s:\n",
+    "    s.layout = neuroglancer.row_layout(\n",
+    "        [neuroglancer.LayerGroupViewer(layers=['image', 'overlay']),\n",
+    "         neuroglancer.LayerGroupViewer(layers=['segmentation'])])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Remove the overlay layer."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "with viewer.txn() as s:\n",
+    "    s.layout = neuroglancer.row_layout(\n",
+    "        [neuroglancer.LayerGroupViewer(layers=['image']),\n",
+    "         neuroglancer.LayerGroupViewer(layers=['segmentation'])])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Create a publicly sharable URL to the viewer state (only works for external data sources, not layers served from Python).  The Python objects for representing the viewer state (`neuroglancer.ViewerState` and friends) can also be used independently from the interactive Python-tied viewer to create Neuroglancer links."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "https://neuroglancer-demo.appspot.com#!%7B%22layers%22:%7B%22image%22:%7B%22source%22:%22precomputed://gs://neuroglancer-public-data/flyem_fib-25/image%22,%22type%22:%22image%22%7D,%22segmentation%22:%7B%22source%22:%22precomputed://gs://neuroglancer-public-data/flyem_fib-25/ground_truth%22,%22type%22:%22segmentation%22,%22segments%22:%5B%2210625%22,%221752%22,%2288847%22%5D%7D%7D,%22navigation%22:%7B%22pose%22:%7B%22position%22:%7B%22voxelSize%22:%5B8,8,8%5D,%22voxelCoordinates%22:%5B3000,3000,3000%5D%7D%7D,%22zoomFactor%22:8%7D,%22perspectiveZoom%22:300,%22layout%22:%7B%22type%22:%22row%22,%22children%22:%5B%7B%22type%22:%22viewer%22,%22layers%22:%5B%22image%22%5D%7D,%7B%22type%22:%22viewer%22,%22layers%22:%5B%22segmentation%22%5D%7D%5D%7D%7D\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(neuroglancer.to_url(viewer.state))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Stop the Neuroglancer web server, which invalidates any existing links to the Python-tied viewer."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "neuroglancer.stop()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 2",
+   "language": "python",
+   "name": "python2"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 2
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython2",
+   "version": "2.7.13"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

docs(python): add Jupyter notebook example