{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "<img src=\"./images/DLI_Header.png\" width=400/>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Fundamentals of Accelerated Data Science # "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 03 - cuGraph Single Source Shortest Path ##\n",
    "\n",
    "**Table of Contents**\n",
    "<br>\n",
    "This notebook GPU-accelerated graph analytics with cuGraph to identify the shortest path from node on the road network to every other node, both by distance, which we will demo, and by time, which you will implement. You will also visualize the results of your findings. This notebook covers the below sections:\n",
    "1. [Environment](#Environment)\n",
    "2. [Loading Data](#Loading-Data)\n",
    "3. [Construct Graph with cuGraph](#Construct-Graph-with-cuGraph)\n",
    "4. [Analyzing the Graph](#Analyzing-the-Graph)\n",
    "5. [Single Source Shortest Path](#Single-Source-Shortest-Path)\n",
    "6. [Analyze a Graph with Time Weights](#Analyze-a-Graph-with-Time-Weights)\n",
    "    * [Exercise #1 - Step 1: Construct the Graph](#Exercise-#1---Step-1:-Construct-the-Graph)\n",
    "    * [Exercise #2 - Step 2: Get Travel Times From a Node to All Others](#Exercise-#2---Step-2:-Get-Travel-Times-From-a-Node-to-All-Others)\n",
    "    * [Visualize the Node Travel Times](#Visualize-the-Node-Travel-Times)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Environment ##"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "import cudf\n",
    "import cugraph as cg\n",
    "\n",
    "import cuxfilter as cxf\n",
    "from bokeh.palettes import Magma, Turbo256, Plasma256, Viridis256"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Loading Data ##"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We start by loading the road graph data you prepared for constructing a graph with cuGraph, with the long unique `nodeid` replaced with simple (and memory-efficient) integers we call the `graph_id`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>src</th>\n",
       "      <th>dst</th>\n",
       "      <th>length</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>129165</td>\n",
       "      <td>44.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>1678323</td>\n",
       "      <td>70.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>2372610</td>\n",
       "      <td>18.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>2483057</td>\n",
       "      <td>40.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>55.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   src      dst  length\n",
       "0    0   129165    44.0\n",
       "1    1  1678323    70.0\n",
       "2    1  2372610    18.0\n",
       "3    1  2483057    40.0\n",
       "4    2        2    55.0"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "road_graph = cudf.read_csv('./data/road_graph.csv', dtype=['int32', 'int32', 'float32'])\n",
    "road_graph.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we load the graph-ready data you prepared that uses amount of time traveled as edge weight."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>src</th>\n",
       "      <th>dst</th>\n",
       "      <th>length_s</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>129165</td>\n",
       "      <td>3.280848</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>1678323</td>\n",
       "      <td>5.219531</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>2372610</td>\n",
       "      <td>1.342165</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>2483057</td>\n",
       "      <td>2.982589</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>4.101060</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   src      dst  length_s\n",
       "0    0   129165  3.280848\n",
       "1    1  1678323  5.219531\n",
       "2    1  2372610  1.342165\n",
       "3    1  2483057  2.982589\n",
       "4    2        2  4.101060"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "speed_graph = cudf.read_csv('./data/road_graph_speed.csv', dtype=['int32', 'int32', 'float32'])\n",
    "speed_graph.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally we import the full `road_nodes` data set, which we will use below for visualizations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>node_id</th>\n",
       "      <th>east</th>\n",
       "      <th>north</th>\n",
       "      <th>type</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>id02FE73D4-E88D-4119-8DC2-6E80DE6F6594</td>\n",
       "      <td>320608.09375</td>\n",
       "      <td>870994.0000</td>\n",
       "      <td>junction</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>id634D65C1-C38B-4868-9080-2E1E47F0935C</td>\n",
       "      <td>320628.50000</td>\n",
       "      <td>871103.8125</td>\n",
       "      <td>road end</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>idDC14D4D1-774E-487D-8EDE-60B129E5482C</td>\n",
       "      <td>320635.46875</td>\n",
       "      <td>870983.8750</td>\n",
       "      <td>junction</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>id51555819-1A39-4B41-B0C9-C6D2086D9921</td>\n",
       "      <td>320648.68750</td>\n",
       "      <td>871083.5625</td>\n",
       "      <td>junction</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>id9E362428-79D7-4EE3-B015-0CE3F6A78A69</td>\n",
       "      <td>320658.18750</td>\n",
       "      <td>871162.3750</td>\n",
       "      <td>junction</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                  node_id          east        north      type\n",
       "0  id02FE73D4-E88D-4119-8DC2-6E80DE6F6594  320608.09375  870994.0000  junction\n",
       "1  id634D65C1-C38B-4868-9080-2E1E47F0935C  320628.50000  871103.8125  road end\n",
       "2  idDC14D4D1-774E-487D-8EDE-60B129E5482C  320635.46875  870983.8750  junction\n",
       "3  id51555819-1A39-4B41-B0C9-C6D2086D9921  320648.68750  871083.5625  junction\n",
       "4  id9E362428-79D7-4EE3-B015-0CE3F6A78A69  320658.18750  871162.3750  junction"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "road_nodes = cudf.read_csv('./data/road_nodes.csv', dtype=['str', 'float32', 'float32', 'str'])\n",
    "road_nodes = road_nodes.drop_duplicates() # again, some road nodes appeared on multiple map tiles in the original source\n",
    "road_nodes.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3078117, 4)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "road_nodes.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3078116"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "speed_graph.src.max()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Construct Graph with cuGraph ##"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have the well-prepped `road_graph` data, we pass it to cuGraph to create our graph data structure, which we can then use for accelerated analysis. In order to do so, we first use cuGraph to instantiate a `Graph` instance, and then pass the instance edge sources, edge destinations, and edge weights, currently the length of the roads."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 154 ms, sys: 52.9 ms, total: 207 ms\n",
      "Wall time: 206 ms\n"
     ]
    }
   ],
   "source": [
    "G = cg.Graph()\n",
    "%time G.from_cudf_edgelist(road_graph, source='src', destination='dst', edge_attr='length')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Analyzing the Graph ##"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, we check the number of nodes and edges in our graph:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3078117"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "G.number_of_nodes()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3620793"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "G.number_of_edges()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also analyze the degrees of our graph nodes. We would expect, as before, that every node would have a degree of 2 or higher, since undirected edges count as two edges (one in, one out) for each of their nodes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "mean     4.689990\n",
       "std      1.913452\n",
       "min      2.000000\n",
       "25%      2.000000\n",
       "50%      6.000000\n",
       "75%      6.000000\n",
       "max     16.000000\n",
       "Name: degree, dtype: float64"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deg_df = G.degree()\n",
    "deg_df['degree'].describe()[1:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We would also expect that every degree would be a multiple of 2, for the same reason. We check that there are no nodes with odd degrees (that is, degrees with a value of 1 modulo 2):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>degree</th>\n",
       "      <th>vertex</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "Empty DataFrame\n",
       "Columns: [degree, vertex]\n",
       "Index: []"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deg_df[deg_df['degree'].mod(2) == 1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Observe for reference that some roads loop from a node back to itself:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>src</th>\n",
       "      <th>dst</th>\n",
       "      <th>length</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>55.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>145</th>\n",
       "      <td>62</td>\n",
       "      <td>62</td>\n",
       "      <td>108.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>293</th>\n",
       "      <td>124</td>\n",
       "      <td>124</td>\n",
       "      <td>67.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>471</th>\n",
       "      <td>196</td>\n",
       "      <td>196</td>\n",
       "      <td>26.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>571</th>\n",
       "      <td>240</td>\n",
       "      <td>240</td>\n",
       "      <td>44.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7216602</th>\n",
       "      <td>3077469</td>\n",
       "      <td>3077469</td>\n",
       "      <td>78.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7216735</th>\n",
       "      <td>3077519</td>\n",
       "      <td>3077519</td>\n",
       "      <td>111.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7216849</th>\n",
       "      <td>3077567</td>\n",
       "      <td>3077567</td>\n",
       "      <td>69.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7217091</th>\n",
       "      <td>3077670</td>\n",
       "      <td>3077670</td>\n",
       "      <td>30.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7217294</th>\n",
       "      <td>3077756</td>\n",
       "      <td>3077756</td>\n",
       "      <td>45.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>23417 rows × 3 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "             src      dst  length\n",
       "4              2        2    55.0\n",
       "145           62       62   108.0\n",
       "293          124      124    67.0\n",
       "471          196      196    26.0\n",
       "571          240      240    44.0\n",
       "...          ...      ...     ...\n",
       "7216602  3077469  3077469    78.0\n",
       "7216735  3077519  3077519   111.0\n",
       "7216849  3077567  3077567    69.0\n",
       "7217091  3077670  3077670    30.0\n",
       "7217294  3077756  3077756    45.0\n",
       "\n",
       "[23417 rows x 3 columns]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "road_graph.loc[road_graph.src == road_graph.dst]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Single Source Shortest Path ##"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To demo the Single Source Shortest Path (SSSP) algorithm, we will start with the node with the highest degree. First we obtain its `graph_id`, reported by the `degree` method as `vertex`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "652907"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "demo_node = deg_df.nlargest(1, 'degree')\n",
    "demo_node_graph_id = demo_node['vertex'].iloc[0]\n",
    "demo_node_graph_id"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now call `cg.sssp`, passing it the entire graph `G`, and the `graph_id` for our selected vertex. Doing so will calculate the shortest path, using the road length weights we have provided, to *every* other node in the graph - millions of paths, in seconds:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 7.56 s, sys: 28.6 ms, total: 7.59 s\n",
      "Wall time: 7.55 s\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>distance</th>\n",
       "      <th>vertex</th>\n",
       "      <th>predecessor</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.0</td>\n",
       "      <td>652907</td>\n",
       "      <td>-1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>110322.0</td>\n",
       "      <td>1252792</td>\n",
       "      <td>1378560</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>213691.0</td>\n",
       "      <td>1606375</td>\n",
       "      <td>1375709</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>106434.0</td>\n",
       "      <td>1826781</td>\n",
       "      <td>2377706</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>253196.0</td>\n",
       "      <td>1990110</td>\n",
       "      <td>1652530</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   distance   vertex  predecessor\n",
       "0       0.0   652907           -1\n",
       "1  110322.0  1252792      1378560\n",
       "2  213691.0  1606375      1375709\n",
       "3  106434.0  1826781      2377706\n",
       "4  253196.0  1990110      1652530"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time shortest_distances_from_demo_node = cg.sssp(G, demo_node_graph_id)\n",
    "shortest_distances_from_demo_node.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "mean    210086.107365\n",
       "std     137145.769283\n",
       "min          0.000000\n",
       "25%     125054.500000\n",
       "50%     181815.500000\n",
       "75%     252472.250000\n",
       "max     868870.500000\n",
       "Name: distance, dtype: float64"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Limiting to those nodes that were connected (within ~4.3 billion meters because\n",
    "# cg.sssp uses the max int value for unreachable nodes, such as those on different islands)\n",
    "shortest_distances_from_demo_node['distance'].loc[shortest_distances_from_demo_node['distance'] < 2**32].describe()[1:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Analyze a Graph with Time Weights ##"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For this exercise, you are going to analyze the graph of GB's roads, but this time, instead of using raw distance for a road's weights, you will be using how long it will take to travel along the road."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise #1 - Step 1: Construct the Graph ###"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Construct a cuGraph graph called `G_ex` using the sources and destinations found in `speed_graph`, along with length in seconds values for the edges' weights."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   src      dst  length_s\n",
      "0    0   129165  3.280848\n",
      "1    1  1678323  5.219531\n",
      "2    1  2372610  1.342165\n",
      "3    1  2483057  2.982589\n",
      "4    2        2  4.101060\n"
     ]
    }
   ],
   "source": [
    "print(speed_graph.head())\n",
    "G_ex = cg.Graph()\n",
    "G_ex.from_cudf_edgelist(speed_graph, source='src', destination='dst', edge_attr='length_s')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise #2 - Step 2: Get Travel Times From a Node to All Others ###"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Choose one of the nodes and calculate the time it would take to travel from it to all other nodes via SSSP, calling the results `ex_dist`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "         degree   vertex\n",
      "0            16   652907\n",
      "1            16  1252792\n",
      "2            14  1606375\n",
      "3            14  1826781\n",
      "4            14  1990110\n",
      "...         ...      ...\n",
      "3078112       2  3078094\n",
      "3078113       2  3078095\n",
      "3078114       2  3078099\n",
      "3078115       2  3078103\n",
      "3078116       2  3078115\n",
      "\n",
      "[3078117 rows x 2 columns]\n",
      "   degree  vertex\n",
      "0      16  652907\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "mean     7424.900285\n",
       "std      4666.250239\n",
       "min         0.000000\n",
       "25%      4484.137695\n",
       "50%      6451.895508\n",
       "75%      9064.045410\n",
       "max     31424.103516\n",
       "Name: distance, dtype: float64"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "degree = G_ex.degree()\n",
    "largest = degree.nlargest(1, 'degree')\n",
    "ex_dist = cg.sssp(G_ex, largest['vertex'].iloc[0])\n",
    "print(degree)\n",
    "print(largest)\n",
    "ex_dist['distance'].loc[dist['distance'] < 2**32].describe()[1:]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Visualize the Node Travel Times ###"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to create a graphic showing the road network by travel time from the selected node, we first need to align the just-calculated distances with their original nodes. For that, we use the mapping from `node_id` strings to their `graph_id` integers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>node_id</th>\n",
       "      <th>graph_id</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>id000000F5-5180-4C03-B05D-B01352C54F89</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>id000003F8-9E09-4829-AD87-6DA4438D22D8</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>id000010DA-C89A-4198-847A-6E62815E038A</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>id000017A0-1843-4BC7-BCF7-C943B6780839</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>id00001B2A-155F-4CD3-8E06-7677ADC6AF74</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                  node_id  graph_id\n",
       "0  id000000F5-5180-4C03-B05D-B01352C54F89         0\n",
       "1  id000003F8-9E09-4829-AD87-6DA4438D22D8         1\n",
       "2  id000010DA-C89A-4198-847A-6E62815E038A         2\n",
       "3  id000017A0-1843-4BC7-BCF7-C943B6780839         3\n",
       "4  id00001B2A-155F-4CD3-8E06-7677ADC6AF74         4"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mapping = cudf.read_csv('./data/node_graph_map.csv')\n",
    "mapping.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see that the `sssp` algorithm has put the `graph_id`s in the `vertex` column, so we will merge on that."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>distance</th>\n",
       "      <th>vertex</th>\n",
       "      <th>predecessor</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.000000</td>\n",
       "      <td>652907</td>\n",
       "      <td>-1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>3753.216553</td>\n",
       "      <td>1252792</td>\n",
       "      <td>423201</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>7388.645020</td>\n",
       "      <td>1606375</td>\n",
       "      <td>1311665</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4061.988281</td>\n",
       "      <td>1826781</td>\n",
       "      <td>1963186</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>9050.323242</td>\n",
       "      <td>1990110</td>\n",
       "      <td>1652530</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      distance   vertex  predecessor\n",
       "0     0.000000   652907           -1\n",
       "1  3753.216553  1252792       423201\n",
       "2  7388.645020  1606375      1311665\n",
       "3  4061.988281  1826781      1963186\n",
       "4  9050.323242  1990110      1652530"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ex_dist.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>node_id</th>\n",
       "      <th>east</th>\n",
       "      <th>north</th>\n",
       "      <th>type</th>\n",
       "      <th>graph_id_x</th>\n",
       "      <th>graph_id_y</th>\n",
       "      <th>graph_id</th>\n",
       "      <th>distance</th>\n",
       "      <th>vertex</th>\n",
       "      <th>predecessor</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>id04231832-AEB2-4C5E-BD56-CAFFAE5D1BA7</td>\n",
       "      <td>371832.03125</td>\n",
       "      <td>862046.5000</td>\n",
       "      <td>junction</td>\n",
       "      <td>49786</td>\n",
       "      <td>49786</td>\n",
       "      <td>49786</td>\n",
       "      <td>23946.572266</td>\n",
       "      <td>49786</td>\n",
       "      <td>2496013</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>id2483B2D5-98C8-4458-8EE9-610FBB6434E1</td>\n",
       "      <td>371598.93750</td>\n",
       "      <td>862171.0625</td>\n",
       "      <td>road end</td>\n",
       "      <td>438690</td>\n",
       "      <td>438690</td>\n",
       "      <td>438690</td>\n",
       "      <td>23966.257812</td>\n",
       "      <td>438690</td>\n",
       "      <td>49786</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>idAAA610C1-858C-4629-A97B-7700DD546C04</td>\n",
       "      <td>373089.59375</td>\n",
       "      <td>862270.2500</td>\n",
       "      <td>junction</td>\n",
       "      <td>2051698</td>\n",
       "      <td>2051698</td>\n",
       "      <td>2051698</td>\n",
       "      <td>24055.251953</td>\n",
       "      <td>2051698</td>\n",
       "      <td>154149</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>idEDE3D1D7-86EE-477F-8A97-22949D207527</td>\n",
       "      <td>372495.09375</td>\n",
       "      <td>862283.5000</td>\n",
       "      <td>junction</td>\n",
       "      <td>2859739</td>\n",
       "      <td>2859739</td>\n",
       "      <td>2859739</td>\n",
       "      <td>24007.380859</td>\n",
       "      <td>2859739</td>\n",
       "      <td>2355879</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>idDE57AF33-B7EA-4CB7-A741-AD58F1CCC267</td>\n",
       "      <td>371846.00000</td>\n",
       "      <td>862292.0000</td>\n",
       "      <td>junction</td>\n",
       "      <td>2672778</td>\n",
       "      <td>2672778</td>\n",
       "      <td>2672778</td>\n",
       "      <td>23955.781250</td>\n",
       "      <td>2672778</td>\n",
       "      <td>49786</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                  node_id          east        north  \\\n",
       "0  id04231832-AEB2-4C5E-BD56-CAFFAE5D1BA7  371832.03125  862046.5000   \n",
       "1  id2483B2D5-98C8-4458-8EE9-610FBB6434E1  371598.93750  862171.0625   \n",
       "2  idAAA610C1-858C-4629-A97B-7700DD546C04  373089.59375  862270.2500   \n",
       "3  idEDE3D1D7-86EE-477F-8A97-22949D207527  372495.09375  862283.5000   \n",
       "4  idDE57AF33-B7EA-4CB7-A741-AD58F1CCC267  371846.00000  862292.0000   \n",
       "\n",
       "       type  graph_id_x  graph_id_y  graph_id      distance   vertex  \\\n",
       "0  junction       49786       49786     49786  23946.572266    49786   \n",
       "1  road end      438690      438690    438690  23966.257812   438690   \n",
       "2  junction     2051698     2051698   2051698  24055.251953  2051698   \n",
       "3  junction     2859739     2859739   2859739  24007.380859  2859739   \n",
       "4  junction     2672778     2672778   2672778  23955.781250  2672778   \n",
       "\n",
       "   predecessor  \n",
       "0      2496013  \n",
       "1        49786  \n",
       "2       154149  \n",
       "3      2355879  \n",
       "4        49786  "
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "road_nodes = road_nodes.merge(mapping, on='node_id')\n",
    "road_nodes = road_nodes.merge(ex_dist, left_on='graph_id', right_on='vertex')\n",
    "road_nodes.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we select those columns we are going to use for the visualization.\n",
    "\n",
    "For color-scaling purposes, we get rid of the unreachable nodes with their extreme distances, and we invert the distance numbers so that brighter pixels indicate closer locations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "gdf = road_nodes[['east', 'north', 'distance']]\n",
    "gdf = gdf[gdf['distance'] < 2**32]\n",
    "gdf['distance'] = gdf['distance'].pow(1/2).mul(-1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Otherwise, this visualization will be largely similar to the scatter plots we made to visualize the population, but instead of coloring by point density as in those cases, we will color by mean travel time to the nodes within a pixel."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "cxf_data = cxf.DataFrame.from_dataframe(gdf)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "heatmap_chart = cxf.charts.datashader.scatter(x='east', y='north', \n",
    "                                              # color_palette=Plasma256, # try also Turbo256, Viridis256, Magma, Plasma256\n",
    "                                              # pixel_shade_type='linear', # can also be log, cbrt, linear\n",
    "                                              aggregate_col='distance',\n",
    "                                              aggregate_fn='mean',\n",
    "                                              # point_shape='square',\n",
    "                                              point_size=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<script type=\"esms-options\">{\"shimMode\": true}</script><style>*[data-root-id],\n",
       "*[data-root-id] > * {\n",
       "  box-sizing: border-box;\n",
       "  font-family: var(--jp-ui-font-family);\n",
       "  font-size: var(--jp-ui-font-size1);\n",
       "  color: var(--vscode-editor-foreground, var(--jp-ui-font-color1));\n",
       "}\n",
       "\n",
       "/* Override VSCode background color */\n",
       ".cell-output-ipywidget-background:has(\n",
       "    > .cell-output-ipywidget-background > .lm-Widget > *[data-root-id]\n",
       "  ),\n",
       ".cell-output-ipywidget-background:has(> .lm-Widget > *[data-root-id]) {\n",
       "  background-color: transparent !important;\n",
       "}\n",
       "</style>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": "(function(root) {\n  function now() {\n    return new Date();\n  }\n\n  const force = true;\n  const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n  const reloading = false;\n  const Bokeh = root.Bokeh;\n\n  // Set a timeout for this load but only if we are not already initializing\n  if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n    root._bokeh_timeout = Date.now() + 5000;\n    root._bokeh_failed_load = false;\n  }\n\n  function run_callbacks() {\n    try {\n      root._bokeh_onload_callbacks.forEach(function(callback) {\n        if (callback != null)\n          callback();\n      });\n    } finally {\n      delete root._bokeh_onload_callbacks;\n    }\n    console.debug(\"Bokeh: all callbacks have finished\");\n  }\n\n  function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n    if (css_urls == null) css_urls = [];\n    if (js_urls == null) js_urls = [];\n    if (js_modules == null) js_modules = [];\n    if (js_exports == null) js_exports = {};\n\n    root._bokeh_onload_callbacks.push(callback);\n\n    if (root._bokeh_is_loading > 0) {\n      // Don't load bokeh if it is still initializing\n      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n      return null;\n    } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n      // There is nothing to load\n      run_callbacks();\n      return null;\n    }\n\n    function on_load() {\n      root._bokeh_is_loading--;\n      if (root._bokeh_is_loading === 0) {\n        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n        run_callbacks()\n      }\n    }\n    window._bokeh_on_load = on_load\n\n    function on_error(e) {\n      const src_el = e.srcElement\n      console.error(\"failed to load \" + (src_el.href || src_el.src));\n    }\n\n    const skip = [];\n    if (window.requirejs) {\n      window.requirejs.config({'packages': {}, 'paths': {'h3': 'https://cdn.jsdelivr.net/npm/h3-js@4.1.0/dist/h3-js.umd', 'deck-gl': 'https://cdn.jsdelivr.net/npm/deck.gl@9.0.20/dist.min', 'deck-json': 'https://cdn.jsdelivr.net/npm/@deck.gl/json@9.0.20/dist.min', 'loader-csv': 'https://cdn.jsdelivr.net/npm/@loaders.gl/csv@4.2.2/dist/dist.min', 'loader-json': 'https://cdn.jsdelivr.net/npm/@loaders.gl/json@4.2.2/dist/dist.min', 'loader-tiles': 'https://cdn.jsdelivr.net/npm/@loaders.gl/3d-tiles@4.2.2/dist/dist.min', 'mapbox-gl': 'https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl', 'carto': 'https://cdn.jsdelivr.net/npm/@deck.gl/carto@^9.0.20/dist.min'}, 'shim': {'deck-json': {'deps': ['deck-gl']}, 'deck-gl': {'deps': ['h3']}}});\n      require([\"h3\"], function(h3) {\n        window.h3 = h3\n        on_load()\n      })\n      require([\"deck-gl\"], function(deck) {\n        window.deck = deck\n        on_load()\n      })\n      require([\"deck-json\"], function() {\n        on_load()\n      })\n      require([\"loader-csv\"], function() {\n        on_load()\n      })\n      require([\"loader-json\"], function() {\n        on_load()\n      })\n      require([\"loader-tiles\"], function() {\n        on_load()\n      })\n      require([\"mapbox-gl\"], function(mapboxgl) {\n        window.mapboxgl = mapboxgl\n        on_load()\n      })\n      require([\"carto\"], function() {\n        on_load()\n      })\n      root._bokeh_is_loading = css_urls.length + 8;\n    } else {\n      root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n    }\n\n    const existing_stylesheets = []\n    const links = document.getElementsByTagName('link')\n    for (let i = 0; i < links.length; i++) {\n      const link = links[i]\n      if (link.href != null) {\n        existing_stylesheets.push(link.href)\n      }\n    }\n    for (let i = 0; i < css_urls.length; i++) {\n      const url = css_urls[i];\n      const escaped = encodeURI(url)\n      if (existing_stylesheets.indexOf(escaped) !== -1) {\n        on_load()\n        continue;\n      }\n      const element = document.createElement(\"link\");\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.rel = \"stylesheet\";\n      element.type = \"text/css\";\n      element.href = url;\n      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n      document.body.appendChild(element);\n    }    if (((window.deck !== undefined) && (!(window.deck instanceof HTMLElement))) || window.requirejs) {\n      var urls = ['https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/h3-js@4.1.0/dist/h3-js.umd.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/deck.gl@9.0.20/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@deck.gl/json@9.0.20/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/csv@4.2.2/dist/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/json@4.2.2/dist/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/3d-tiles@4.2.2/dist/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/mapbox-gl-js/v3.0.1/mapbox-gl.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/maplibre-gl/dist/maplibre-gl.js'];\n      for (var i = 0; i < urls.length; i++) {\n        skip.push(encodeURI(urls[i]))\n      }\n    }    if (((window.mapboxgl !== undefined) && (!(window.mapboxgl instanceof HTMLElement))) || window.requirejs) {\n      var urls = ['https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@deck.gl/carto@^9.0.20/dist.min.js'];\n      for (var i = 0; i < urls.length; i++) {\n        skip.push(encodeURI(urls[i]))\n      }\n    }    var existing_scripts = []\n    const scripts = document.getElementsByTagName('script')\n    for (let i = 0; i < scripts.length; i++) {\n      var script = scripts[i]\n      if (script.src != null) {\n        existing_scripts.push(script.src)\n      }\n    }\n    for (let i = 0; i < js_urls.length; i++) {\n      const url = js_urls[i];\n      const escaped = encodeURI(url)\n      if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n        if (!window.requirejs) {\n          on_load();\n        }\n        continue;\n      }\n      const element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.async = false;\n      element.src = url;\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n    for (let i = 0; i < js_modules.length; i++) {\n      const url = js_modules[i];\n      const escaped = encodeURI(url)\n      if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n        if (!window.requirejs) {\n          on_load();\n        }\n        continue;\n      }\n      var element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.async = false;\n      element.src = url;\n      element.type = \"module\";\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n    for (const name in js_exports) {\n      const url = js_exports[name];\n      const escaped = encodeURI(url)\n      if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n        if (!window.requirejs) {\n          on_load();\n        }\n        continue;\n      }\n      var element = document.createElement('script');\n      element.onerror = on_error;\n      element.async = false;\n      element.type = \"module\";\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      element.textContent = `\n      import ${name} from \"${url}\"\n      window.${name} = ${name}\n      window._bokeh_on_load()\n      `\n      document.head.appendChild(element);\n    }\n    if (!js_urls.length && !js_modules.length) {\n      on_load()\n    }\n  };\n\n  function inject_raw_css(css) {\n    const element = document.createElement(\"style\");\n    element.appendChild(document.createTextNode(css));\n    document.body.appendChild(element);\n  }\n\n  const js_urls = [\"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/h3-js@4.1.0/dist/h3-js.umd.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/deck.gl@9.0.20/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@deck.gl/json@9.0.20/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/csv@4.2.2/dist/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/json@4.2.2/dist/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/3d-tiles@4.2.2/dist/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/mapbox-gl-js/v3.0.1/mapbox-gl.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/maplibre-gl/dist/maplibre-gl.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@deck.gl/carto@^9.0.20/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/panel.min.js\"];\n  const js_modules = [];\n  const js_exports = {};\n  const css_urls = [\"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/mapbox-gl-js/v3.0.1/mapbox-gl.css?v=1.5.2\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/maplibre-gl@4.4.1/dist/maplibre-gl.css?v=1.5.2\"];\n  const inline_js = [    function(Bokeh) {\n      inject_raw_css(\"\\n.dataframe table{\\n  border: none;\\n}\\n\\n.panel-df table{\\n    width: 100%;\\n    border-collapse: collapse;\\n    border: none;\\n}\\n.panel-df td{\\n    white-space: nowrap;\\n    overflow: auto;\\n    text-overflow: ellipsis;\\n}\\n\");\n    },    function(Bokeh) {\n      inject_raw_css(\"\\n.multi-select{\\n    color: white;\\n    z-index: 100;\\n    background: rgba(44,43,43,0.5);\\n    border-radius: 1px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n.multi-select > .bk {\\n    padding: 5px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n\\n.deck-chart {\\n    z-index: 10;\\n    position: initial !important;\\n}\\n\");\n    },    function(Bokeh) {\n      inject_raw_css(\"\\n.center-header {\\n    text-align: center\\n}\\n.bk-input-group {\\n    padding: 10px;\\n}\\n#sidebar {\\n    padding-top: 10px;\\n}\\n.custom-widget-box {\\n    margin-top: 20px;\\n    padding: 5px;\\n    border: None !important;\\n}\\n.custom-widget-box > p {\\n    margin: 0px;\\n}\\n.bk-input-group {\\n    color: None !important;\\n}\\n.indicator {\\n    text-align: center;\\n}\\n.widget-card {\\n    margin: 5px 10px;\\n}\\n.number-card {\\n    margin: 5px 10px;\\n    text-align: center;\\n}\\n.number-card-value {\\n    width: 100%;\\n    margin: 0px;\\n}\\n\");\n    },    function(Bokeh) {\n      Bokeh.set_log_level(\"info\");\n    },\n    function(Bokeh) {\n      (function(root, factory) {\n          factory(root[\"Bokeh\"]);\n      })(this, function(Bokeh) {\n        let define;\n        return (function outer(modules, entry) {\n        if (Bokeh != null) {\n          return Bokeh.register_plugin(modules, entry);\n        } else {\n          throw new Error(\"Cannot find Bokeh. You have to load it prior to loading plugins.\");\n        }\n      })\n      ({\n        \"custom/main\": function(require, module, exports) {\n          const models = {\n            \"CustomInspectTool\": require(\"custom/cuxfilter.charts.datashader.custom_extensions.graph_inspect_widget.custom_inspect_tool\").CustomInspectTool\n          };\n          require(\"base\").register_models(models);\n          module.exports = models;\n        },\n        \"custom/cuxfilter.charts.datashader.custom_extensions.graph_inspect_widget.custom_inspect_tool\": function(require, module, exports) {\n      \"use strict\";\n      var _a;\n      Object.defineProperty(exports, \"__esModule\", { value: true });\n      exports.CustomInspectTool = exports.CustomInspectToolView = void 0;\n      const inspect_tool_1 = require(\"models/tools/inspectors/inspect_tool\");\n      class CustomInspectToolView extends inspect_tool_1.InspectToolView {\n          connect_signals() {\n              super.connect_signals();\n              this.on_change([this.model.properties.active], () => {\n                  this.model._active = this.model.active;\n              });\n          }\n      }\n      exports.CustomInspectToolView = CustomInspectToolView;\n      CustomInspectToolView.__name__ = \"CustomInspectToolView\";\n      class CustomInspectTool extends inspect_tool_1.InspectTool {\n          constructor(attrs) {\n              super(attrs);\n          }\n      }\n      exports.CustomInspectTool = CustomInspectTool;\n      _a = CustomInspectTool;\n      CustomInspectTool.__name__ = \"CustomInspectTool\";\n      (() => {\n          _a.prototype.default_view = CustomInspectToolView;\n          _a.define(({ Boolean }) => ({\n              _active: [Boolean, true]\n          }));\n          _a.register_alias(\"customInspect\", () => new _a());\n      })();\n      //# sourceMappingURL=graph_inspect_widget.py:CustomInspectTool.js.map\n      }\n      }, \"custom/main\");\n      ;\n      });\n\n    },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n  ];\n\n  function run_inline_js() {\n    if ((root.Bokeh !== undefined) || (force === true)) {\n      for (let i = 0; i < inline_js.length; i++) {\n        try {\n          inline_js[i].call(root, root.Bokeh);\n        } catch(e) {\n          if (!reloading) {\n            throw e;\n          }\n        }\n      }\n      // Cache old bokeh versions\n      if (Bokeh != undefined && !reloading) {\n        var NewBokeh = root.Bokeh;\n        if (Bokeh.versions === undefined) {\n          Bokeh.versions = new Map();\n        }\n        if (NewBokeh.version !== Bokeh.version) {\n          Bokeh.versions.set(NewBokeh.version, NewBokeh)\n        }\n        root.Bokeh = Bokeh;\n      }\n    } else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(run_inline_js, 100);\n    } else if (!root._bokeh_failed_load) {\n      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n      root._bokeh_failed_load = true;\n    }\n    root._bokeh_is_initializing = false\n  }\n\n  function load_or_wait() {\n    // Implement a backoff loop that tries to ensure we do not load multiple\n    // versions of Bokeh and its dependencies at the same time.\n    // In recent versions we use the root._bokeh_is_initializing flag\n    // to determine whether there is an ongoing attempt to initialize\n    // bokeh, however for backward compatibility we also try to ensure\n    // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n    // before older versions are fully initialized.\n    if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n      // If the timeout and bokeh was not successfully loaded we reset\n      // everything and try loading again\n      root._bokeh_timeout = Date.now() + 5000;\n      root._bokeh_is_initializing = false;\n      root._bokeh_onload_callbacks = undefined;\n      root._bokeh_is_loading = 0\n      console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n      load_or_wait();\n    } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n      setTimeout(load_or_wait, 100);\n    } else {\n      root._bokeh_is_initializing = true\n      root._bokeh_onload_callbacks = []\n      const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n      if (!reloading && !bokeh_loaded) {\n        if (root.Bokeh) {\n          root.Bokeh = undefined;\n        }\n        console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n      }\n      load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n        console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n        run_inline_js();\n      });\n    }\n  }\n  // Give older versions of the autoload script a head-start to ensure\n  // they initialize before we start loading newer version.\n  setTimeout(load_or_wait, 100)\n}(window));",
      "application/vnd.holoviews_load.v0+json": ""
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n  window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n    function JupyterCommManager() {\n    }\n\n    JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n      if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n        comm_manager.register_target(comm_id, function(comm) {\n          comm.on_msg(msg_handler);\n        });\n      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n        window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n          comm.onMsg = msg_handler;\n        });\n      } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n        google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n          var messages = comm.messages[Symbol.asyncIterator]();\n          function processIteratorResult(result) {\n            var message = result.value;\n            console.log(message)\n            var content = {data: message.data, comm_id};\n            var buffers = []\n            for (var buffer of message.buffers || []) {\n              buffers.push(new DataView(buffer))\n            }\n            var metadata = message.metadata || {};\n            var msg = {content, buffers, metadata}\n            msg_handler(msg);\n            return messages.next().then(processIteratorResult);\n          }\n          return messages.next().then(processIteratorResult);\n        })\n      }\n    }\n\n    JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n      if (comm_id in window.PyViz.comms) {\n        return window.PyViz.comms[comm_id];\n      } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n        var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n        if (msg_handler) {\n          comm.on_msg(msg_handler);\n        }\n      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n        var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n        comm.open();\n        if (msg_handler) {\n          comm.onMsg = msg_handler;\n        }\n      } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n        var comm_promise = google.colab.kernel.comms.open(comm_id)\n        comm_promise.then((comm) => {\n          window.PyViz.comms[comm_id] = comm;\n          if (msg_handler) {\n            var messages = comm.messages[Symbol.asyncIterator]();\n            function processIteratorResult(result) {\n              var message = result.value;\n              var content = {data: message.data};\n              var metadata = message.metadata || {comm_id};\n              var msg = {content, metadata}\n              msg_handler(msg);\n              return messages.next().then(processIteratorResult);\n            }\n            return messages.next().then(processIteratorResult);\n          }\n        }) \n        var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n          return comm_promise.then((comm) => {\n            comm.send(data, metadata, buffers, disposeOnDone);\n          });\n        };\n        var comm = {\n          send: sendClosure\n        };\n      }\n      window.PyViz.comms[comm_id] = comm;\n      return comm;\n    }\n    window.PyViz.comm_manager = new JupyterCommManager();\n    \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n  var div = document.createElement(\"div\");\n  var script = document.createElement(\"script\");\n  node.appendChild(div);\n  node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n  var output_area = handle.output_area;\n  var output = handle.output;\n  if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n    return\n  }\n  var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n  var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n  if (id !== undefined) {\n    var nchildren = toinsert.length;\n    var html_node = toinsert[nchildren-1].children[0];\n    html_node.innerHTML = output.data[HTML_MIME_TYPE];\n    var scripts = [];\n    var nodelist = html_node.querySelectorAll(\"script\");\n    for (var i in nodelist) {\n      if (nodelist.hasOwnProperty(i)) {\n        scripts.push(nodelist[i])\n      }\n    }\n\n    scripts.forEach( function (oldScript) {\n      var newScript = document.createElement(\"script\");\n      var attrs = [];\n      var nodemap = oldScript.attributes;\n      for (var j in nodemap) {\n        if (nodemap.hasOwnProperty(j)) {\n          attrs.push(nodemap[j])\n        }\n      }\n      attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n      newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n      oldScript.parentNode.replaceChild(newScript, oldScript);\n    });\n    if (JS_MIME_TYPE in output.data) {\n      toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n    }\n    output_area._hv_plot_id = id;\n    if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n      window.PyViz.plot_index[id] = Bokeh.index[id];\n    } else {\n      window.PyViz.plot_index[id] = null;\n    }\n  } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n    var bk_div = document.createElement(\"div\");\n    bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n    var script_attrs = bk_div.children[0].attributes;\n    for (var i = 0; i < script_attrs.length; i++) {\n      toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n    }\n    // store reference to server id on output_area\n    output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n  }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n  var id = handle.cell.output_area._hv_plot_id;\n  var server_id = handle.cell.output_area._bokeh_server_id;\n  if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n  var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n  if (server_id !== null) {\n    comm.send({event_type: 'server_delete', 'id': server_id});\n    return;\n  } else if (comm !== null) {\n    comm.send({event_type: 'delete', 'id': id});\n  }\n  delete PyViz.plot_index[id];\n  if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n    var doc = window.Bokeh.index[id].model.document\n    doc.clear();\n    const i = window.Bokeh.documents.indexOf(doc);\n    if (i > -1) {\n      window.Bokeh.documents.splice(i, 1);\n    }\n  }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n  delete PyViz.comms[\"hv-extension-comm\"];\n  window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n  handle_clear_output(event, {cell: {output_area: handle.output_area}})\n  handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n  function append_mime(data, metadata, element) {\n    // create a DOM node to render to\n    var toinsert = this.create_output_subarea(\n    metadata,\n    CLASS_NAME,\n    EXEC_MIME_TYPE\n    );\n    this.keyboard_manager.register_events(toinsert);\n    // Render to node\n    var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n    render(props, toinsert[0]);\n    element.append(toinsert);\n    return toinsert\n  }\n\n  events.on('output_added.OutputArea', handle_add_output);\n  events.on('output_updated.OutputArea', handle_update_output);\n  events.on('clear_output.CodeCell', handle_clear_output);\n  events.on('delete.Cell', handle_clear_output);\n  events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n  OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n    safe: true,\n    index: 0\n  });\n}\n\nif (window.Jupyter !== undefined) {\n  try {\n    var events = require('base/js/events');\n    var OutputArea = require('notebook/js/outputarea').OutputArea;\n    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n      register_renderer(events, OutputArea);\n    }\n  } catch(err) {\n  }\n}\n",
      "application/vnd.holoviews_load.v0+json": ""
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<script type=\"esms-options\">{\"shimMode\": true}</script><style>*[data-root-id],\n",
       "*[data-root-id] > * {\n",
       "  box-sizing: border-box;\n",
       "  font-family: var(--jp-ui-font-family);\n",
       "  font-size: var(--jp-ui-font-size1);\n",
       "  color: var(--vscode-editor-foreground, var(--jp-ui-font-color1));\n",
       "}\n",
       "\n",
       "/* Override VSCode background color */\n",
       ".cell-output-ipywidget-background:has(\n",
       "    > .cell-output-ipywidget-background > .lm-Widget > *[data-root-id]\n",
       "  ),\n",
       ".cell-output-ipywidget-background:has(> .lm-Widget > *[data-root-id]) {\n",
       "  background-color: transparent !important;\n",
       "}\n",
       "</style>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": "(function(root) {\n  function now() {\n    return new Date();\n  }\n\n  const force = false;\n  const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n  const reloading = true;\n  const Bokeh = root.Bokeh;\n\n  // Set a timeout for this load but only if we are not already initializing\n  if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n    root._bokeh_timeout = Date.now() + 5000;\n    root._bokeh_failed_load = false;\n  }\n\n  function run_callbacks() {\n    try {\n      root._bokeh_onload_callbacks.forEach(function(callback) {\n        if (callback != null)\n          callback();\n      });\n    } finally {\n      delete root._bokeh_onload_callbacks;\n    }\n    console.debug(\"Bokeh: all callbacks have finished\");\n  }\n\n  function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n    if (css_urls == null) css_urls = [];\n    if (js_urls == null) js_urls = [];\n    if (js_modules == null) js_modules = [];\n    if (js_exports == null) js_exports = {};\n\n    root._bokeh_onload_callbacks.push(callback);\n\n    if (root._bokeh_is_loading > 0) {\n      // Don't load bokeh if it is still initializing\n      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n      return null;\n    } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n      // There is nothing to load\n      run_callbacks();\n      return null;\n    }\n\n    function on_load() {\n      root._bokeh_is_loading--;\n      if (root._bokeh_is_loading === 0) {\n        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n        run_callbacks()\n      }\n    }\n    window._bokeh_on_load = on_load\n\n    function on_error(e) {\n      const src_el = e.srcElement\n      console.error(\"failed to load \" + (src_el.href || src_el.src));\n    }\n\n    const skip = [];\n    if (window.requirejs) {\n      window.requirejs.config({'packages': {}, 'paths': {'h3': 'https://cdn.jsdelivr.net/npm/h3-js@4.1.0/dist/h3-js.umd', 'deck-gl': 'https://cdn.jsdelivr.net/npm/deck.gl@9.0.20/dist.min', 'deck-json': 'https://cdn.jsdelivr.net/npm/@deck.gl/json@9.0.20/dist.min', 'loader-csv': 'https://cdn.jsdelivr.net/npm/@loaders.gl/csv@4.2.2/dist/dist.min', 'loader-json': 'https://cdn.jsdelivr.net/npm/@loaders.gl/json@4.2.2/dist/dist.min', 'loader-tiles': 'https://cdn.jsdelivr.net/npm/@loaders.gl/3d-tiles@4.2.2/dist/dist.min', 'mapbox-gl': 'https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl', 'carto': 'https://cdn.jsdelivr.net/npm/@deck.gl/carto@^9.0.20/dist.min'}, 'shim': {'deck-json': {'deps': ['deck-gl']}, 'deck-gl': {'deps': ['h3']}}});\n      require([\"h3\"], function(h3) {\n        window.h3 = h3\n        on_load()\n      })\n      require([\"deck-gl\"], function(deck) {\n        window.deck = deck\n        on_load()\n      })\n      require([\"deck-json\"], function() {\n        on_load()\n      })\n      require([\"loader-csv\"], function() {\n        on_load()\n      })\n      require([\"loader-json\"], function() {\n        on_load()\n      })\n      require([\"loader-tiles\"], function() {\n        on_load()\n      })\n      require([\"mapbox-gl\"], function(mapboxgl) {\n        window.mapboxgl = mapboxgl\n        on_load()\n      })\n      require([\"carto\"], function() {\n        on_load()\n      })\n      root._bokeh_is_loading = css_urls.length + 8;\n    } else {\n      root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n    }\n\n    const existing_stylesheets = []\n    const links = document.getElementsByTagName('link')\n    for (let i = 0; i < links.length; i++) {\n      const link = links[i]\n      if (link.href != null) {\n        existing_stylesheets.push(link.href)\n      }\n    }\n    for (let i = 0; i < css_urls.length; i++) {\n      const url = css_urls[i];\n      const escaped = encodeURI(url)\n      if (existing_stylesheets.indexOf(escaped) !== -1) {\n        on_load()\n        continue;\n      }\n      const element = document.createElement(\"link\");\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.rel = \"stylesheet\";\n      element.type = \"text/css\";\n      element.href = url;\n      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n      document.body.appendChild(element);\n    }    if (((window.deck !== undefined) && (!(window.deck instanceof HTMLElement))) || window.requirejs) {\n      var urls = ['https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/h3-js@4.1.0/dist/h3-js.umd.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/deck.gl@9.0.20/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@deck.gl/json@9.0.20/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/csv@4.2.2/dist/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/json@4.2.2/dist/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/3d-tiles@4.2.2/dist/dist.min.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/mapbox-gl-js/v3.0.1/mapbox-gl.js', 'https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/maplibre-gl/dist/maplibre-gl.js'];\n      for (var i = 0; i < urls.length; i++) {\n        skip.push(encodeURI(urls[i]))\n      }\n    }    if (((window.mapboxgl !== undefined) && (!(window.mapboxgl instanceof HTMLElement))) || window.requirejs) {\n      var urls = ['https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@deck.gl/carto@^9.0.20/dist.min.js'];\n      for (var i = 0; i < urls.length; i++) {\n        skip.push(encodeURI(urls[i]))\n      }\n    }    var existing_scripts = []\n    const scripts = document.getElementsByTagName('script')\n    for (let i = 0; i < scripts.length; i++) {\n      var script = scripts[i]\n      if (script.src != null) {\n        existing_scripts.push(script.src)\n      }\n    }\n    for (let i = 0; i < js_urls.length; i++) {\n      const url = js_urls[i];\n      const escaped = encodeURI(url)\n      if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n        if (!window.requirejs) {\n          on_load();\n        }\n        continue;\n      }\n      const element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.async = false;\n      element.src = url;\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n    for (let i = 0; i < js_modules.length; i++) {\n      const url = js_modules[i];\n      const escaped = encodeURI(url)\n      if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n        if (!window.requirejs) {\n          on_load();\n        }\n        continue;\n      }\n      var element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error;\n      element.async = false;\n      element.src = url;\n      element.type = \"module\";\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n    for (const name in js_exports) {\n      const url = js_exports[name];\n      const escaped = encodeURI(url)\n      if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n        if (!window.requirejs) {\n          on_load();\n        }\n        continue;\n      }\n      var element = document.createElement('script');\n      element.onerror = on_error;\n      element.async = false;\n      element.type = \"module\";\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      element.textContent = `\n      import ${name} from \"${url}\"\n      window.${name} = ${name}\n      window._bokeh_on_load()\n      `\n      document.head.appendChild(element);\n    }\n    if (!js_urls.length && !js_modules.length) {\n      on_load()\n    }\n  };\n\n  function inject_raw_css(css) {\n    const element = document.createElement(\"style\");\n    element.appendChild(document.createTextNode(css));\n    document.body.appendChild(element);\n  }\n\n  const js_urls = [\"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/h3-js@4.1.0/dist/h3-js.umd.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/deck.gl@9.0.20/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@deck.gl/json@9.0.20/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/csv@4.2.2/dist/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/json@4.2.2/dist/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@loaders.gl/3d-tiles@4.2.2/dist/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/mapbox-gl-js/v3.0.1/mapbox-gl.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/maplibre-gl/dist/maplibre-gl.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/@deck.gl/carto@^9.0.20/dist.min.js\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\"];\n  const js_modules = [];\n  const js_exports = {};\n  const css_urls = [\"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/mapbox-gl-js/v3.0.1/mapbox-gl.css?v=1.5.2\", \"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/deckglplot/maplibre-gl@4.4.1/dist/maplibre-gl.css?v=1.5.2\"];\n  const inline_js = [    function(Bokeh) {\n      inject_raw_css(\"\\n.dataframe table{\\n  border: none;\\n}\\n\\n.panel-df table{\\n    width: 100%;\\n    border-collapse: collapse;\\n    border: none;\\n}\\n.panel-df td{\\n    white-space: nowrap;\\n    overflow: auto;\\n    text-overflow: ellipsis;\\n}\\n\");\n    },    function(Bokeh) {\n      inject_raw_css(\"\\n.multi-select{\\n    color: white;\\n    z-index: 100;\\n    background: rgba(44,43,43,0.5);\\n    border-radius: 1px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n.multi-select > .bk {\\n    padding: 5px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n\\n.deck-chart {\\n    z-index: 10;\\n    position: initial !important;\\n}\\n\");\n    },    function(Bokeh) {\n      inject_raw_css(\"\\n.center-header {\\n    text-align: center\\n}\\n.bk-input-group {\\n    padding: 10px;\\n}\\n#sidebar {\\n    padding-top: 10px;\\n}\\n.custom-widget-box {\\n    margin-top: 20px;\\n    padding: 5px;\\n    border: None !important;\\n}\\n.custom-widget-box > p {\\n    margin: 0px;\\n}\\n.bk-input-group {\\n    color: None !important;\\n}\\n.indicator {\\n    text-align: center;\\n}\\n.widget-card {\\n    margin: 5px 10px;\\n}\\n.number-card {\\n    margin: 5px 10px;\\n    text-align: center;\\n}\\n.number-card-value {\\n    width: 100%;\\n    margin: 0px;\\n}\\n\");\n    },    function(Bokeh) {\n      Bokeh.set_log_level(\"info\");\n    },\n    function(Bokeh) {\n      (function(root, factory) {\n          factory(root[\"Bokeh\"]);\n      })(this, function(Bokeh) {\n        let define;\n        return (function outer(modules, entry) {\n        if (Bokeh != null) {\n          return Bokeh.register_plugin(modules, entry);\n        } else {\n          throw new Error(\"Cannot find Bokeh. You have to load it prior to loading plugins.\");\n        }\n      })\n      ({\n        \"custom/main\": function(require, module, exports) {\n          const models = {\n            \"CustomInspectTool\": require(\"custom/cuxfilter.charts.datashader.custom_extensions.graph_inspect_widget.custom_inspect_tool\").CustomInspectTool\n          };\n          require(\"base\").register_models(models);\n          module.exports = models;\n        },\n        \"custom/cuxfilter.charts.datashader.custom_extensions.graph_inspect_widget.custom_inspect_tool\": function(require, module, exports) {\n      \"use strict\";\n      var _a;\n      Object.defineProperty(exports, \"__esModule\", { value: true });\n      exports.CustomInspectTool = exports.CustomInspectToolView = void 0;\n      const inspect_tool_1 = require(\"models/tools/inspectors/inspect_tool\");\n      class CustomInspectToolView extends inspect_tool_1.InspectToolView {\n          connect_signals() {\n              super.connect_signals();\n              this.on_change([this.model.properties.active], () => {\n                  this.model._active = this.model.active;\n              });\n          }\n      }\n      exports.CustomInspectToolView = CustomInspectToolView;\n      CustomInspectToolView.__name__ = \"CustomInspectToolView\";\n      class CustomInspectTool extends inspect_tool_1.InspectTool {\n          constructor(attrs) {\n              super(attrs);\n          }\n      }\n      exports.CustomInspectTool = CustomInspectTool;\n      _a = CustomInspectTool;\n      CustomInspectTool.__name__ = \"CustomInspectTool\";\n      (() => {\n          _a.prototype.default_view = CustomInspectToolView;\n          _a.define(({ Boolean }) => ({\n              _active: [Boolean, true]\n          }));\n          _a.register_alias(\"customInspect\", () => new _a());\n      })();\n      //# sourceMappingURL=graph_inspect_widget.py:CustomInspectTool.js.map\n      }\n      }, \"custom/main\");\n      ;\n      });\n\n    },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n  ];\n\n  function run_inline_js() {\n    if ((root.Bokeh !== undefined) || (force === true)) {\n      for (let i = 0; i < inline_js.length; i++) {\n        try {\n          inline_js[i].call(root, root.Bokeh);\n        } catch(e) {\n          if (!reloading) {\n            throw e;\n          }\n        }\n      }\n      // Cache old bokeh versions\n      if (Bokeh != undefined && !reloading) {\n        var NewBokeh = root.Bokeh;\n        if (Bokeh.versions === undefined) {\n          Bokeh.versions = new Map();\n        }\n        if (NewBokeh.version !== Bokeh.version) {\n          Bokeh.versions.set(NewBokeh.version, NewBokeh)\n        }\n        root.Bokeh = Bokeh;\n      }\n    } else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(run_inline_js, 100);\n    } else if (!root._bokeh_failed_load) {\n      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n      root._bokeh_failed_load = true;\n    }\n    root._bokeh_is_initializing = false\n  }\n\n  function load_or_wait() {\n    // Implement a backoff loop that tries to ensure we do not load multiple\n    // versions of Bokeh and its dependencies at the same time.\n    // In recent versions we use the root._bokeh_is_initializing flag\n    // to determine whether there is an ongoing attempt to initialize\n    // bokeh, however for backward compatibility we also try to ensure\n    // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n    // before older versions are fully initialized.\n    if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n      // If the timeout and bokeh was not successfully loaded we reset\n      // everything and try loading again\n      root._bokeh_timeout = Date.now() + 5000;\n      root._bokeh_is_initializing = false;\n      root._bokeh_onload_callbacks = undefined;\n      root._bokeh_is_loading = 0\n      console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n      load_or_wait();\n    } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n      setTimeout(load_or_wait, 100);\n    } else {\n      root._bokeh_is_initializing = true\n      root._bokeh_onload_callbacks = []\n      const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n      if (!reloading && !bokeh_loaded) {\n        if (root.Bokeh) {\n          root.Bokeh = undefined;\n        }\n        console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n      }\n      load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n        console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n        run_inline_js();\n      });\n    }\n  }\n  // Give older versions of the autoload script a head-start to ensure\n  // they initialize before we start loading newer version.\n  setTimeout(load_or_wait, 100)\n}(window));",
      "application/vnd.holoviews_load.v0+json": ""
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n  window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n    function JupyterCommManager() {\n    }\n\n    JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n      if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n        comm_manager.register_target(comm_id, function(comm) {\n          comm.on_msg(msg_handler);\n        });\n      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n        window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n          comm.onMsg = msg_handler;\n        });\n      } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n        google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n          var messages = comm.messages[Symbol.asyncIterator]();\n          function processIteratorResult(result) {\n            var message = result.value;\n            console.log(message)\n            var content = {data: message.data, comm_id};\n            var buffers = []\n            for (var buffer of message.buffers || []) {\n              buffers.push(new DataView(buffer))\n            }\n            var metadata = message.metadata || {};\n            var msg = {content, buffers, metadata}\n            msg_handler(msg);\n            return messages.next().then(processIteratorResult);\n          }\n          return messages.next().then(processIteratorResult);\n        })\n      }\n    }\n\n    JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n      if (comm_id in window.PyViz.comms) {\n        return window.PyViz.comms[comm_id];\n      } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n        var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n        var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n        if (msg_handler) {\n          comm.on_msg(msg_handler);\n        }\n      } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n        var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n        comm.open();\n        if (msg_handler) {\n          comm.onMsg = msg_handler;\n        }\n      } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n        var comm_promise = google.colab.kernel.comms.open(comm_id)\n        comm_promise.then((comm) => {\n          window.PyViz.comms[comm_id] = comm;\n          if (msg_handler) {\n            var messages = comm.messages[Symbol.asyncIterator]();\n            function processIteratorResult(result) {\n              var message = result.value;\n              var content = {data: message.data};\n              var metadata = message.metadata || {comm_id};\n              var msg = {content, metadata}\n              msg_handler(msg);\n              return messages.next().then(processIteratorResult);\n            }\n            return messages.next().then(processIteratorResult);\n          }\n        }) \n        var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n          return comm_promise.then((comm) => {\n            comm.send(data, metadata, buffers, disposeOnDone);\n          });\n        };\n        var comm = {\n          send: sendClosure\n        };\n      }\n      window.PyViz.comms[comm_id] = comm;\n      return comm;\n    }\n    window.PyViz.comm_manager = new JupyterCommManager();\n    \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n  var div = document.createElement(\"div\");\n  var script = document.createElement(\"script\");\n  node.appendChild(div);\n  node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n  var output_area = handle.output_area;\n  var output = handle.output;\n  if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n    return\n  }\n  var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n  var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n  if (id !== undefined) {\n    var nchildren = toinsert.length;\n    var html_node = toinsert[nchildren-1].children[0];\n    html_node.innerHTML = output.data[HTML_MIME_TYPE];\n    var scripts = [];\n    var nodelist = html_node.querySelectorAll(\"script\");\n    for (var i in nodelist) {\n      if (nodelist.hasOwnProperty(i)) {\n        scripts.push(nodelist[i])\n      }\n    }\n\n    scripts.forEach( function (oldScript) {\n      var newScript = document.createElement(\"script\");\n      var attrs = [];\n      var nodemap = oldScript.attributes;\n      for (var j in nodemap) {\n        if (nodemap.hasOwnProperty(j)) {\n          attrs.push(nodemap[j])\n        }\n      }\n      attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n      newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n      oldScript.parentNode.replaceChild(newScript, oldScript);\n    });\n    if (JS_MIME_TYPE in output.data) {\n      toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n    }\n    output_area._hv_plot_id = id;\n    if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n      window.PyViz.plot_index[id] = Bokeh.index[id];\n    } else {\n      window.PyViz.plot_index[id] = null;\n    }\n  } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n    var bk_div = document.createElement(\"div\");\n    bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n    var script_attrs = bk_div.children[0].attributes;\n    for (var i = 0; i < script_attrs.length; i++) {\n      toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n    }\n    // store reference to server id on output_area\n    output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n  }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n  var id = handle.cell.output_area._hv_plot_id;\n  var server_id = handle.cell.output_area._bokeh_server_id;\n  if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n  var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n  if (server_id !== null) {\n    comm.send({event_type: 'server_delete', 'id': server_id});\n    return;\n  } else if (comm !== null) {\n    comm.send({event_type: 'delete', 'id': id});\n  }\n  delete PyViz.plot_index[id];\n  if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n    var doc = window.Bokeh.index[id].model.document\n    doc.clear();\n    const i = window.Bokeh.documents.indexOf(doc);\n    if (i > -1) {\n      window.Bokeh.documents.splice(i, 1);\n    }\n  }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n  delete PyViz.comms[\"hv-extension-comm\"];\n  window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n  handle_clear_output(event, {cell: {output_area: handle.output_area}})\n  handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n  function append_mime(data, metadata, element) {\n    // create a DOM node to render to\n    var toinsert = this.create_output_subarea(\n    metadata,\n    CLASS_NAME,\n    EXEC_MIME_TYPE\n    );\n    this.keyboard_manager.register_events(toinsert);\n    // Render to node\n    var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n    render(props, toinsert[0]);\n    element.append(toinsert);\n    return toinsert\n  }\n\n  events.on('output_added.OutputArea', handle_add_output);\n  events.on('output_updated.OutputArea', handle_update_output);\n  events.on('clear_output.CodeCell', handle_clear_output);\n  events.on('delete.Cell', handle_clear_output);\n  events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n  OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n    safe: true,\n    index: 0\n  });\n}\n\nif (window.Jupyter !== undefined) {\n  try {\n    var events = require('base/js/events');\n    var OutputArea = require('notebook/js/outputarea').OutputArea;\n    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n      register_renderer(events, OutputArea);\n    }\n  } catch(err) {\n  }\n}\n",
      "application/vnd.holoviews_load.v0+json": ""
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.holoviews_exec.v0+json": "",
      "text/html": [
       "<div id='728a83e3-6647-42d0-8aec-6531cb4bb23e'>\n",
       "  <div id=\"f2eafbf0-c2f2-42f2-b651-ab8d7e8a9ffe\" data-root-id=\"728a83e3-6647-42d0-8aec-6531cb4bb23e\" style=\"display: contents;\"></div>\n",
       "</div>\n",
       "<script type=\"application/javascript\">(function(root) {\n",
       "  var docs_json = {\"86be7803-b49c-4ee5-a88f-7cb686db9cf7\":{\"version\":\"3.5.2\",\"title\":\"Bokeh Application\",\"roots\":[{\"type\":\"object\",\"name\":\"panel.models.browser.BrowserInfo\",\"id\":\"728a83e3-6647-42d0-8aec-6531cb4bb23e\"},{\"type\":\"object\",\"name\":\"panel.models.comm_manager.CommManager\",\"id\":\"7fda0d0f-ecb5-4442-9afd-5947172491d6\",\"attributes\":{\"plot_id\":\"728a83e3-6647-42d0-8aec-6531cb4bb23e\",\"comm_id\":\"735a8f71757d4189a4dd02abe28d20bb\",\"client_comm_id\":\"cb95fa6e51ff4194b0e26295667efcc3\"}}],\"defs\":[{\"type\":\"model\",\"name\":\"ReactiveHTML1\"},{\"type\":\"model\",\"name\":\"FlexBox1\",\"properties\":[{\"name\":\"align_content\",\"kind\":\"Any\",\"default\":\"flex-start\"},{\"name\":\"align_items\",\"kind\":\"Any\",\"default\":\"flex-start\"},{\"name\":\"flex_direction\",\"kind\":\"Any\",\"default\":\"row\"},{\"name\":\"flex_wrap\",\"kind\":\"Any\",\"default\":\"wrap\"},{\"name\":\"gap\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"justify_content\",\"kind\":\"Any\",\"default\":\"flex-start\"}]},{\"type\":\"model\",\"name\":\"FloatPanel1\",\"properties\":[{\"name\":\"config\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"contained\",\"kind\":\"Any\",\"default\":true},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"right-top\"},{\"name\":\"offsetx\",\"kind\":\"Any\",\"default\":null},{\"name\":\"offsety\",\"kind\":\"Any\",\"default\":null},{\"name\":\"theme\",\"kind\":\"Any\",\"default\":\"primary\"},{\"name\":\"status\",\"kind\":\"Any\",\"default\":\"normalized\"}]},{\"type\":\"model\",\"name\":\"GridStack1\",\"properties\":[{\"name\":\"mode\",\"kind\":\"Any\",\"default\":\"warn\"},{\"name\":\"ncols\",\"kind\":\"Any\",\"default\":null},{\"name\":\"nrows\",\"kind\":\"Any\",\"default\":null},{\"name\":\"allow_resize\",\"kind\":\"Any\",\"default\":true},{\"name\":\"allow_drag\",\"kind\":\"Any\",\"default\":true},{\"name\":\"state\",\"kind\":\"Any\",\"default\":[]}]},{\"type\":\"model\",\"name\":\"drag1\",\"properties\":[{\"name\":\"slider_width\",\"kind\":\"Any\",\"default\":5},{\"name\":\"slider_color\",\"kind\":\"Any\",\"default\":\"black\"},{\"name\":\"value\",\"kind\":\"Any\",\"default\":50}]},{\"type\":\"model\",\"name\":\"click1\",\"properties\":[{\"name\":\"terminal_output\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"debug_name\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"clears\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"FastWrapper1\",\"properties\":[{\"name\":\"object\",\"kind\":\"Any\",\"default\":null},{\"name\":\"style\",\"kind\":\"Any\",\"default\":null}]},{\"type\":\"model\",\"name\":\"NotificationAreaBase1\",\"properties\":[{\"name\":\"js_events\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"bottom-right\"},{\"name\":\"_clear\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"NotificationArea1\",\"properties\":[{\"name\":\"js_events\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"notifications\",\"kind\":\"Any\",\"default\":[]},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"bottom-right\"},{\"name\":\"_clear\",\"kind\":\"Any\",\"default\":0},{\"name\":\"types\",\"kind\":\"Any\",\"default\":[{\"type\":\"map\",\"entries\":[[\"type\",\"warning\"],[\"background\",\"#ffc107\"],[\"icon\",{\"type\":\"map\",\"entries\":[[\"className\",\"fas fa-exclamation-triangle\"],[\"tagName\",\"i\"],[\"color\",\"white\"]]}]]},{\"type\":\"map\",\"entries\":[[\"type\",\"info\"],[\"background\",\"#007bff\"],[\"icon\",{\"type\":\"map\",\"entries\":[[\"className\",\"fas fa-info-circle\"],[\"tagName\",\"i\"],[\"color\",\"white\"]]}]]}]}]},{\"type\":\"model\",\"name\":\"Notification\",\"properties\":[{\"name\":\"background\",\"kind\":\"Any\",\"default\":null},{\"name\":\"duration\",\"kind\":\"Any\",\"default\":3000},{\"name\":\"icon\",\"kind\":\"Any\",\"default\":null},{\"name\":\"message\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"notification_type\",\"kind\":\"Any\",\"default\":null},{\"name\":\"_destroyed\",\"kind\":\"Any\",\"default\":false}]},{\"type\":\"model\",\"name\":\"TemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"BootstrapTemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"TemplateEditor1\",\"properties\":[{\"name\":\"layout\",\"kind\":\"Any\",\"default\":[]}]},{\"type\":\"model\",\"name\":\"MaterialTemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"ReactiveESM1\"},{\"type\":\"model\",\"name\":\"JSComponent1\"},{\"type\":\"model\",\"name\":\"ReactComponent1\"},{\"type\":\"model\",\"name\":\"AnyWidgetComponent1\"},{\"type\":\"model\",\"name\":\"request_value1\",\"properties\":[{\"name\":\"fill\",\"kind\":\"Any\",\"default\":\"none\"},{\"name\":\"_synced\",\"kind\":\"Any\",\"default\":null},{\"name\":\"_request_sync\",\"kind\":\"Any\",\"default\":0}]}]}};\n",
       "  var render_items = [{\"docid\":\"86be7803-b49c-4ee5-a88f-7cb686db9cf7\",\"roots\":{\"728a83e3-6647-42d0-8aec-6531cb4bb23e\":\"f2eafbf0-c2f2-42f2-b651-ab8d7e8a9ffe\"},\"root_ids\":[\"728a83e3-6647-42d0-8aec-6531cb4bb23e\"]}];\n",
       "  var docs = Object.values(docs_json)\n",
       "  if (!docs) {\n",
       "    return\n",
       "  }\n",
       "  const py_version = docs[0].version.replace('rc', '-rc.').replace('.dev', '-dev.')\n",
       "  async function embed_document(root) {\n",
       "    var Bokeh = get_bokeh(root)\n",
       "    await Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "    for (const render_item of render_items) {\n",
       "      for (const root_id of render_item.root_ids) {\n",
       "\tconst id_el = document.getElementById(root_id)\n",
       "\tif (id_el.children.length && id_el.children[0].hasAttribute('data-root-id')) {\n",
       "\t  const root_el = id_el.children[0]\n",
       "\t  root_el.id = root_el.id + '-rendered'\n",
       "\t  for (const child of root_el.children) {\n",
       "            // Ensure JupyterLab does not capture keyboard shortcuts\n",
       "            // see: https://jupyterlab.readthedocs.io/en/4.1.x/extension/notebook.html#keyboard-interaction-model\n",
       "\t    child.setAttribute('data-lm-suppress-shortcuts', 'true')\n",
       "\t  }\n",
       "\t}\n",
       "      }\n",
       "    }\n",
       "  }\n",
       "  function get_bokeh(root) {\n",
       "    if (root.Bokeh === undefined) {\n",
       "      return null\n",
       "    } else if (root.Bokeh.version !== py_version) {\n",
       "      if (root.Bokeh.versions === undefined || !root.Bokeh.versions.has(py_version)) {\n",
       "\treturn null\n",
       "      }\n",
       "      return root.Bokeh.versions.get(py_version);\n",
       "    } else if (root.Bokeh.version === py_version) {\n",
       "      return root.Bokeh\n",
       "    }\n",
       "    return null\n",
       "  }\n",
       "  function is_loaded(root) {\n",
       "    var Bokeh = get_bokeh(root)\n",
       "    return (Bokeh != null && Bokeh.Panel !== undefined && ( root.deck !== undefined) && ( root.deck !== undefined))\n",
       "  }\n",
       "  if (is_loaded(root)) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (is_loaded(root)) {\n",
       "        clearInterval(timer);\n",
       "        embed_document(root);\n",
       "      } else if (document.readyState == \"complete\") {\n",
       "        attempts++;\n",
       "        if (attempts > 200) {\n",
       "          clearInterval(timer);\n",
       "\t  var Bokeh = get_bokeh(root)\n",
       "\t  if (Bokeh == null || Bokeh.Panel == null) {\n",
       "            console.warn(\"Panel: ERROR: Unable to run Panel code because Bokeh or Panel library is missing\");\n",
       "\t  } else {\n",
       "\t    console.warn(\"Panel: WARNING: Attempting to render but not all required libraries could be resolved.\")\n",
       "\t    embed_document(root)\n",
       "\t  }\n",
       "        }\n",
       "      }\n",
       "    }, 25, root)\n",
       "  }\n",
       "})(window);</script>"
      ]
     },
     "metadata": {
      "application/vnd.holoviews_exec.v0+json": {
       "id": "728a83e3-6647-42d0-8aec-6531cb4bb23e"
      }
     },
     "output_type": "display_data"
    },
    {
     "data": {},
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {},
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.holoviews_exec.v0+json": "",
      "text/html": [
       "<div id='78fe5a6f-6244-4012-b442-379c3e93ad4c'>\n",
       "  <div id=\"bfb51cce-757f-42c8-bf92-8f2c7337efaa\" data-root-id=\"78fe5a6f-6244-4012-b442-379c3e93ad4c\" style=\"display: contents;\"></div>\n",
       "</div>\n",
       "<script type=\"application/javascript\">(function(root) {\n",
       "  var docs_json = {\"a4457f2d-fba5-4e12-a508-aa4a084eaa1e\":{\"version\":\"3.5.2\",\"title\":\"Bokeh Application\",\"roots\":[{\"type\":\"object\",\"name\":\"GridBox\",\"id\":\"78fe5a6f-6244-4012-b442-379c3e93ad4c\",\"attributes\":{\"rows\":null,\"cols\":null,\"name\":\"GridSpec00981\",\"stylesheets\":[\"\\n:host(.pn-loading):before, .pn-loading:before {\\n  background-color: #c3c3c3;\\n  mask-size: auto calc(min(50%, 400px));\\n  -webkit-mask-size: auto calc(min(50%, 400px));\\n}\",{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"5179eb89-c822-4e9e-9a08-eda5214b7b88\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.5.2/dist/css/loading.css\"}},\"\\n.dataframe table{\\n  border: none;\\n}\\n\\n.panel-df table{\\n    width: 100%;\\n    border-collapse: collapse;\\n    border: none;\\n}\\n.panel-df td{\\n    white-space: nowrap;\\n    overflow: auto;\\n    text-overflow: ellipsis;\\n}\\n\",\"\\n.multi-select{\\n    color: white;\\n    z-index: 100;\\n    background: rgba(44,43,43,0.5);\\n    border-radius: 1px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n.multi-select > .bk {\\n    padding: 5px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n\\n.deck-chart {\\n    z-index: 10;\\n    position: initial !important;\\n}\\n\",\"\\n.center-header {\\n    text-align: center\\n}\\n.bk-input-group {\\n    padding: 10px;\\n}\\n#sidebar {\\n    padding-top: 10px;\\n}\\n.custom-widget-box {\\n    margin-top: 20px;\\n    padding: 5px;\\n    border: None !important;\\n}\\n.custom-widget-box > p {\\n    margin: 0px;\\n}\\n.bk-input-group {\\n    color: None !important;\\n}\\n.indicator {\\n    text-align: center;\\n}\\n.widget-card {\\n    margin: 5px 10px;\\n}\\n.number-card {\\n    margin: 5px 10px;\\n    text-align: center;\\n}\\n.number-card-value {\\n    width: 100%;\\n    margin: 0px;\\n}\\n\",{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"58f4c9e9-9f25-48e3-b1f3-07de24f3511b\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.5.2/dist/css/gridspec.css\"}},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"b17c8d70-a9c4-4944-a08c-09ed41407ad6\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/theme/default.css\"}},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"3d632975-aa99-4991-9456-6df7e4303fdb\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/theme/native.css\"}}],\"min_width\":1200,\"margin\":0,\"sizing_mode\":\"stretch_width\",\"align\":\"start\",\"children\":[[{\"type\":\"object\",\"name\":\"GridBox\",\"id\":\"77474577-bc7e-4025-b0df-6e6f015d382e\",\"attributes\":{\"rows\":null,\"cols\":null,\"name\":\"GridSpec00893\",\"stylesheets\":[\"\\n:host(.pn-loading):before, .pn-loading:before {\\n  background-color: #c3c3c3;\\n  mask-size: auto calc(min(50%, 400px));\\n  -webkit-mask-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"5179eb89-c822-4e9e-9a08-eda5214b7b88\"},\"\\n.dataframe table{\\n  border: none;\\n}\\n\\n.panel-df table{\\n    width: 100%;\\n    border-collapse: collapse;\\n    border: none;\\n}\\n.panel-df td{\\n    white-space: nowrap;\\n    overflow: auto;\\n    text-overflow: ellipsis;\\n}\\n\",\"\\n.multi-select{\\n    color: white;\\n    z-index: 100;\\n    background: rgba(44,43,43,0.5);\\n    border-radius: 1px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n.multi-select > .bk {\\n    padding: 5px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n\\n.deck-chart {\\n    z-index: 10;\\n    position: initial !important;\\n}\\n\",\"\\n.center-header {\\n    text-align: center\\n}\\n.bk-input-group {\\n    padding: 10px;\\n}\\n#sidebar {\\n    padding-top: 10px;\\n}\\n.custom-widget-box {\\n    margin-top: 20px;\\n    padding: 5px;\\n    border: None !important;\\n}\\n.custom-widget-box > p {\\n    margin: 0px;\\n}\\n.bk-input-group {\\n    color: None !important;\\n}\\n.indicator {\\n    text-align: center;\\n}\\n.widget-card {\\n    margin: 5px 10px;\\n}\\n.number-card {\\n    margin: 5px 10px;\\n    text-align: center;\\n}\\n.number-card-value {\\n    width: 100%;\\n    margin: 0px;\\n}\\n\",{\"id\":\"58f4c9e9-9f25-48e3-b1f3-07de24f3511b\"},{\"id\":\"b17c8d70-a9c4-4944-a08c-09ed41407ad6\"},{\"id\":\"3d632975-aa99-4991-9456-6df7e4303fdb\"}],\"width\":1200,\"height\":800,\"margin\":0,\"sizing_mode\":\"fixed\",\"align\":\"start\",\"children\":[[{\"type\":\"object\",\"name\":\"Figure\",\"id\":\"f4b33f54-83e4-4a12-b5fd-5dd1bf7c7011\",\"attributes\":{\"subscribed_events\":{\"type\":\"set\",\"entries\":[\"selectiongeometry\",\"reset\",\"rangesupdate\"]},\"width\":1200,\"height\":800,\"margin\":[5,10],\"sizing_mode\":\"stretch_both\",\"align\":\"start\",\"x_range\":{\"type\":\"object\",\"name\":\"Range1d\",\"id\":\"ba3b1bfd-7468-48cd-a15a-a9bed69fcdae\",\"attributes\":{\"tags\":[[[\"east\",null]],[]],\"start\":113294.6328125,\"end\":655550.0,\"reset_start\":113294.6328125,\"reset_end\":655550.0}},\"y_range\":{\"type\":\"object\",\"name\":\"Range1d\",\"id\":\"5511a041-af65-4442-9219-12f1cd38697f\",\"attributes\":{\"tags\":[[[\"north\",null]],{\"type\":\"map\",\"entries\":[[\"invert_yaxis\",false],[\"autorange\",false]]}],\"start\":11495.76953125,\"end\":976765.25,\"reset_start\":11495.76953125,\"reset_end\":976765.25}},\"x_scale\":{\"type\":\"object\",\"name\":\"LinearScale\",\"id\":\"9800cd3d-7219-40be-ab4b-e882ea85a900\"},\"y_scale\":{\"type\":\"object\",\"name\":\"LinearScale\",\"id\":\"9bc04d40-ae54-44c9-9c72-275261d7dfd8\"},\"title\":{\"type\":\"object\",\"name\":\"Title\",\"id\":\"7d73a98b-c307-4a8e-a012-42c216f15b65\",\"attributes\":{\"text\":\"Scatter plot for distance mean\",\"text_color\":\"black\",\"text_font_size\":\"12pt\"}},\"renderers\":[{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"eeea670e-d921-4db7-851b-68e8a722f0c6\",\"attributes\":{\"data_source\":{\"type\":\"object\",\"name\":\"ColumnDataSource\",\"id\":\"7a9117a1-f3da-42d9-b794-74550f792d2e\",\"attributes\":{\"selected\":{\"type\":\"object\",\"name\":\"Selection\",\"id\":\"0bb365a2-e8f0-42a7-9860-8e31dcd88a5f\",\"attributes\":{\"indices\":[],\"line_indices\":[]}},\"selection_policy\":{\"type\":\"object\",\"name\":\"UnionRenderers\",\"id\":\"0ab65168-bd04-409e-975f-33c9d6190e9f\"},\"data\":{\"type\":\"map\",\"entries\":[[\"x\",[113294.6328125]],[\"y\",[11495.76953125]],[\"dw\",[542255.3671875]],[\"dh\",[965269.48046875]],[\"image\",[{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"[encoded data, 1706668 characters]\"},\"shape\":[400,400],\"dtype\":\"float64\",\"order\":\"little\"}]]]}}},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"d7298381-76c5-4fb9-9798-93a05bb0d276\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"fa44ec7d-56c8-4249-aaa1-92d7929cc135\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Image\",\"id\":\"c07e5187-bc51-46a3-a7a9-acf31342071b\",\"attributes\":{\"tags\":[\"apply_ranges\"],\"x\":{\"type\":\"field\",\"field\":\"x\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"dw\":{\"type\":\"field\",\"field\":\"dw\"},\"dh\":{\"type\":\"field\",\"field\":\"dh\"},\"image\":{\"type\":\"field\",\"field\":\"image\"},\"color_mapper\":{\"type\":\"object\",\"name\":\"EqHistColorMapper\",\"id\":\"1c8e23c8-cb47-4cf7-b8ab-6120b8cc7437\",\"attributes\":{\"palette\":[\"#08306b\",\"#08519c\",\"#2171b5\",\"#4292c6\",\"#6baed6\",\"#9ecae1\",\"#c6dbef\",\"#deebf7\",\"#f7fbff\"],\"nan_color\":\"rgba(0, 0, 0, 0)\",\"low\":-177.26844787597656,\"high\":-0.0,\"rescale_discrete_levels\":true}}}},\"selection_glyph\":{\"type\":\"object\",\"name\":\"Image\",\"id\":\"a08d5e79-b13a-4cee-b0da-95633f2d6b28\",\"attributes\":{\"tags\":[\"apply_ranges\"],\"x\":{\"type\":\"field\",\"field\":\"x\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"dw\":{\"type\":\"field\",\"field\":\"dw\"},\"dh\":{\"type\":\"field\",\"field\":\"dh\"},\"global_alpha\":{\"type\":\"value\",\"value\":1},\"image\":{\"type\":\"field\",\"field\":\"image\"},\"color_mapper\":{\"id\":\"1c8e23c8-cb47-4cf7-b8ab-6120b8cc7437\"}}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Image\",\"id\":\"0c4eedd2-bdcc-4f8c-b16f-3c4ca6452767\",\"attributes\":{\"tags\":[\"apply_ranges\"],\"x\":{\"type\":\"field\",\"field\":\"x\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"dw\":{\"type\":\"field\",\"field\":\"dw\"},\"dh\":{\"type\":\"field\",\"field\":\"dh\"},\"global_alpha\":{\"type\":\"value\",\"value\":1},\"image\":{\"type\":\"field\",\"field\":\"image\"},\"color_mapper\":{\"id\":\"1c8e23c8-cb47-4cf7-b8ab-6120b8cc7437\"}}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Image\",\"id\":\"80fd68db-4045-492a-989a-3456aeff933d\",\"attributes\":{\"tags\":[\"apply_ranges\"],\"x\":{\"type\":\"field\",\"field\":\"x\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"dw\":{\"type\":\"field\",\"field\":\"dw\"},\"dh\":{\"type\":\"field\",\"field\":\"dh\"},\"global_alpha\":{\"type\":\"value\",\"value\":1},\"image\":{\"type\":\"field\",\"field\":\"image\"},\"color_mapper\":{\"id\":\"1c8e23c8-cb47-4cf7-b8ab-6120b8cc7437\"}}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"d95f404c-f942-48a4-a250-9ef6f1479717\",\"attributes\":{\"data_source\":{\"type\":\"object\",\"name\":\"ColumnDataSource\",\"id\":\"a0f444b4-d186-4a60-8bc7-24fe4acf71d2\",\"attributes\":{\"selected\":{\"type\":\"object\",\"name\":\"Selection\",\"id\":\"e5fda4f9-5c18-40c0-8018-d6b2513b61ef\",\"attributes\":{\"indices\":[],\"line_indices\":[]}},\"selection_policy\":{\"type\":\"object\",\"name\":\"UnionRenderers\",\"id\":\"c7157316-8e27-4ac8-96ed-aa191f64f515\"},\"data\":{\"type\":\"map\",\"entries\":[[\"x\",[113294.6328125]],[\"y\",[11495.76953125]],[\"dw\",[542255.3671875]],[\"dh\",[965269.48046875]],[\"image\",[{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"[encoded data, 1706668 characters]\"},\"shape\":[400,400],\"dtype\":\"float64\",\"order\":\"little\"}]]]}}},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"9fe86911-30bb-4073-a81d-364a21df43bf\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"eab339f0-0258-4e21-bea9-3b512123d7c8\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Image\",\"id\":\"d22b7de2-eae9-4f51-9b81-8d44c55b7620\",\"attributes\":{\"tags\":[\"apply_ranges\"],\"x\":{\"type\":\"field\",\"field\":\"x\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"dw\":{\"type\":\"field\",\"field\":\"dw\"},\"dh\":{\"type\":\"field\",\"field\":\"dh\"},\"global_alpha\":{\"type\":\"value\",\"value\":0.2},\"image\":{\"type\":\"field\",\"field\":\"image\"},\"color_mapper\":{\"type\":\"object\",\"name\":\"EqHistColorMapper\",\"id\":\"e502698e-03c6-4127-bc48-bfef7bdd3b50\",\"attributes\":{\"palette\":[\"#08306b\",\"#08519c\",\"#2171b5\",\"#4292c6\",\"#6baed6\",\"#9ecae1\",\"#c6dbef\",\"#deebf7\",\"#f7fbff\"],\"nan_color\":\"rgba(0, 0, 0, 0)\",\"low\":1.0,\"high\":1202.0,\"rescale_discrete_levels\":true}}}},\"selection_glyph\":{\"type\":\"object\",\"name\":\"Image\",\"id\":\"75fd5b51-dc04-49b7-967e-345454230348\",\"attributes\":{\"tags\":[\"apply_ranges\"],\"x\":{\"type\":\"field\",\"field\":\"x\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"dw\":{\"type\":\"field\",\"field\":\"dw\"},\"dh\":{\"type\":\"field\",\"field\":\"dh\"},\"global_alpha\":{\"type\":\"value\",\"value\":0.2},\"image\":{\"type\":\"field\",\"field\":\"image\"},\"color_mapper\":{\"id\":\"e502698e-03c6-4127-bc48-bfef7bdd3b50\"}}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Image\",\"id\":\"5631d53e-a3a9-43da-8df1-b26d5efca862\",\"attributes\":{\"tags\":[\"apply_ranges\"],\"x\":{\"type\":\"field\",\"field\":\"x\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"dw\":{\"type\":\"field\",\"field\":\"dw\"},\"dh\":{\"type\":\"field\",\"field\":\"dh\"},\"global_alpha\":{\"type\":\"value\",\"value\":0.2},\"image\":{\"type\":\"field\",\"field\":\"image\"},\"color_mapper\":{\"id\":\"e502698e-03c6-4127-bc48-bfef7bdd3b50\"}}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Image\",\"id\":\"1409387a-21c1-46c5-84d5-91483278c7ea\",\"attributes\":{\"tags\":[\"apply_ranges\"],\"x\":{\"type\":\"field\",\"field\":\"x\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"dw\":{\"type\":\"field\",\"field\":\"dw\"},\"dh\":{\"type\":\"field\",\"field\":\"dh\"},\"global_alpha\":{\"type\":\"value\",\"value\":0.2},\"image\":{\"type\":\"field\",\"field\":\"image\"},\"color_mapper\":{\"id\":\"e502698e-03c6-4127-bc48-bfef7bdd3b50\"}}}}}],\"toolbar\":{\"type\":\"object\",\"name\":\"Toolbar\",\"id\":\"72f08b34-e9a1-439c-aab0-f7b7147f1aca\",\"attributes\":{\"tools\":[{\"type\":\"object\",\"name\":\"WheelZoomTool\",\"id\":\"79cdfa6b-2670-45c7-bb6f-e80a8bc1dc08\",\"attributes\":{\"tags\":[\"hv_created\"],\"renderers\":\"auto\",\"zoom_together\":\"none\"}},{\"type\":\"object\",\"name\":\"PanTool\",\"id\":\"cc35159c-5b54-49fd-865c-eec657e9dcfd\"},{\"type\":\"object\",\"name\":\"ResetTool\",\"id\":\"2f124053-3be9-4e3e-820f-7fdae703273a\"},{\"type\":\"object\",\"name\":\"BoxSelectTool\",\"id\":\"727db152-6bfa-4fae-bb97-fd3fbaedf0e3\",\"attributes\":{\"renderers\":\"auto\",\"overlay\":{\"type\":\"object\",\"name\":\"BoxAnnotation\",\"id\":\"7e9858fe-a9f0-4221-a45e-60d9e1622450\",\"attributes\":{\"syncable\":false,\"line_color\":\"black\",\"line_alpha\":1.0,\"line_width\":2,\"line_dash\":[4,4],\"fill_color\":\"lightgrey\",\"fill_alpha\":0.5,\"level\":\"overlay\",\"visible\":false,\"left\":{\"type\":\"number\",\"value\":\"nan\"},\"right\":{\"type\":\"number\",\"value\":\"nan\"},\"top\":{\"type\":\"number\",\"value\":\"nan\"},\"bottom\":{\"type\":\"number\",\"value\":\"nan\"},\"editable\":true,\"handles\":{\"type\":\"object\",\"name\":\"BoxInteractionHandles\",\"id\":\"1059c2aa-1e1f-4672-8bf7-9ef0f257266a\",\"attributes\":{\"all\":{\"type\":\"object\",\"name\":\"AreaVisuals\",\"id\":\"d1a0299b-c8b1-47cb-b80c-b4f19d2da6e6\",\"attributes\":{\"fill_color\":\"white\",\"hover_fill_color\":\"lightgray\"}}}}}}}},{\"type\":\"object\",\"name\":\"LassoSelectTool\",\"id\":\"64d8c1d1-f615-4b62-82b8-4b4e4a8f1482\",\"attributes\":{\"renderers\":\"auto\",\"overlay\":{\"type\":\"object\",\"name\":\"PolyAnnotation\",\"id\":\"9ad3965b-a67d-459f-b31c-a7a28d43803d\",\"attributes\":{\"syncable\":false,\"level\":\"overlay\",\"visible\":false,\"xs\":[],\"ys\":[],\"editable\":true,\"line_color\":\"black\",\"line_alpha\":1.0,\"line_width\":2,\"line_dash\":[4,4],\"fill_color\":\"lightgrey\",\"fill_alpha\":0.5}}}},{\"type\":\"object\",\"name\":\"SaveTool\",\"id\":\"2adeda70-254b-4593-a788-5c638de29ed4\"}],\"active_drag\":{\"id\":\"cc35159c-5b54-49fd-865c-eec657e9dcfd\"},\"active_scroll\":{\"id\":\"79cdfa6b-2670-45c7-bb6f-e80a8bc1dc08\"}}},\"left\":[{\"type\":\"object\",\"name\":\"LinearAxis\",\"id\":\"5b4453c9-ba49-4764-b7ef-de58d4fc0244\",\"attributes\":{\"visible\":false,\"ticker\":{\"type\":\"object\",\"name\":\"BasicTicker\",\"id\":\"2fba81ba-15c7-4c10-8c96-dfcf160b92e1\",\"attributes\":{\"mantissas\":[1,2,5]}},\"formatter\":{\"type\":\"object\",\"name\":\"BasicTickFormatter\",\"id\":\"baacab40-11a1-4435-af20-5c239c4a33df\"},\"axis_label\":\"north\",\"major_label_policy\":{\"type\":\"object\",\"name\":\"AllLabels\",\"id\":\"63cb030b-6072-4027-bacd-d2a98cdf97b6\"}}}],\"below\":[{\"type\":\"object\",\"name\":\"LinearAxis\",\"id\":\"7de52917-e775-4441-b9a7-4a683d6710c3\",\"attributes\":{\"visible\":false,\"ticker\":{\"type\":\"object\",\"name\":\"BasicTicker\",\"id\":\"7130fadf-2739-4cdc-abbd-01ca19171f02\",\"attributes\":{\"mantissas\":[1,2,5]}},\"formatter\":{\"type\":\"object\",\"name\":\"BasicTickFormatter\",\"id\":\"be02b846-d8a4-42c0-ac25-611fb83bf340\"},\"axis_label\":\"east\",\"major_label_policy\":{\"type\":\"object\",\"name\":\"AllLabels\",\"id\":\"208ec13d-2483-4f5a-8751-acb698f3dd7a\"}}}],\"center\":[{\"type\":\"object\",\"name\":\"Grid\",\"id\":\"a3c9aff3-502c-486c-a2e4-15ad3210e1ce\",\"attributes\":{\"axis\":{\"id\":\"7de52917-e775-4441-b9a7-4a683d6710c3\"},\"grid_line_color\":null}},{\"type\":\"object\",\"name\":\"Grid\",\"id\":\"c9376d38-75cd-4dcd-a428-ab9301e09441\",\"attributes\":{\"dimension\":1,\"axis\":{\"id\":\"5b4453c9-ba49-4764-b7ef-de58d4fc0244\"},\"grid_line_color\":null}},{\"type\":\"object\",\"name\":\"ColorBar\",\"id\":\"777b4f84-3992-44ed-9838-0524549c30df\",\"attributes\":{\"ticker\":{\"type\":\"object\",\"name\":\"BinnedTicker\",\"id\":\"b52d2da3-2900-4700-85b6-65d3ad679e55\",\"attributes\":{\"mapper\":{\"id\":\"1c8e23c8-cb47-4cf7-b8ab-6120b8cc7437\"}}},\"major_label_policy\":{\"type\":\"object\",\"name\":\"NoOverlap\",\"id\":\"7fd5136f-e908-4e42-a840-8704722f89c9\"},\"label_standoff\":8,\"major_tick_line_color\":\"black\",\"bar_line_color\":\"black\",\"color_mapper\":{\"id\":\"1c8e23c8-cb47-4cf7-b8ab-6120b8cc7437\"}}}],\"min_border_top\":10,\"min_border_bottom\":10,\"min_border_left\":10,\"min_border_right\":10,\"output_backend\":\"webgl\",\"hold_render\":false}},0,0,5,12]]}},0,2,1,10],[{\"type\":\"object\",\"name\":\"panel.models.layout.Column\",\"id\":\"13125d44-7a35-47e0-93c0-32da61f62503\",\"attributes\":{\"name\":\"WidgetBox00892\",\"css_classes\":[\"panel-widget-box\"],\"styles\":{\"type\":\"map\",\"entries\":[[\"border-color\",\"#888888\"]]},\"stylesheets\":[\"\\n:host(.pn-loading):before, .pn-loading:before {\\n  background-color: #c3c3c3;\\n  mask-size: auto calc(min(50%, 400px));\\n  -webkit-mask-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"5179eb89-c822-4e9e-9a08-eda5214b7b88\"},\"\\n.dataframe table{\\n  border: none;\\n}\\n\\n.panel-df table{\\n    width: 100%;\\n    border-collapse: collapse;\\n    border: none;\\n}\\n.panel-df td{\\n    white-space: nowrap;\\n    overflow: auto;\\n    text-overflow: ellipsis;\\n}\\n\",\"\\n.multi-select{\\n    color: white;\\n    z-index: 100;\\n    background: rgba(44,43,43,0.5);\\n    border-radius: 1px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n.multi-select > .bk {\\n    padding: 5px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n\\n.deck-chart {\\n    z-index: 10;\\n    position: initial !important;\\n}\\n\",\"\\n.center-header {\\n    text-align: center\\n}\\n.bk-input-group {\\n    padding: 10px;\\n}\\n#sidebar {\\n    padding-top: 10px;\\n}\\n.custom-widget-box {\\n    margin-top: 20px;\\n    padding: 5px;\\n    border: None !important;\\n}\\n.custom-widget-box > p {\\n    margin: 0px;\\n}\\n.bk-input-group {\\n    color: None !important;\\n}\\n.indicator {\\n    text-align: center;\\n}\\n.widget-card {\\n    margin: 5px 10px;\\n}\\n.number-card {\\n    margin: 5px 10px;\\n    text-align: center;\\n}\\n.number-card-value {\\n    width: 100%;\\n    margin: 0px;\\n}\\n\",{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"b908fa5e-f908-428e-b156-b661acea5852\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.5.2/dist/css/widgetbox.css\"}},{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"11c25991-1eef-4112-873d-66b8d414364f\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.5.2/dist/css/listpanel.css\"}},{\"id\":\"b17c8d70-a9c4-4944-a08c-09ed41407ad6\"},{\"id\":\"3d632975-aa99-4991-9456-6df7e4303fdb\"}],\"margin\":0,\"sizing_mode\":\"stretch_width\",\"align\":\"start\",\"children\":[{\"type\":\"object\",\"name\":\"panel.models.markup.HTML\",\"id\":\"91a93943-e0bc-4f3d-ae44-8024018c5241\",\"attributes\":{\"css_classes\":[\"indicator\"],\"stylesheets\":[\"\\n:host(.pn-loading):before, .pn-loading:before {\\n  background-color: #c3c3c3;\\n  mask-size: auto calc(min(50%, 400px));\\n  -webkit-mask-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"5179eb89-c822-4e9e-9a08-eda5214b7b88\"},\"\\n.dataframe table{\\n  border: none;\\n}\\n\\n.panel-df table{\\n    width: 100%;\\n    border-collapse: collapse;\\n    border: none;\\n}\\n.panel-df td{\\n    white-space: nowrap;\\n    overflow: auto;\\n    text-overflow: ellipsis;\\n}\\n\",\"\\n.multi-select{\\n    color: white;\\n    z-index: 100;\\n    background: rgba(44,43,43,0.5);\\n    border-radius: 1px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n.multi-select > .bk {\\n    padding: 5px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n\\n.deck-chart {\\n    z-index: 10;\\n    position: initial !important;\\n}\\n\",\"\\n.center-header {\\n    text-align: center\\n}\\n.bk-input-group {\\n    padding: 10px;\\n}\\n#sidebar {\\n    padding-top: 10px;\\n}\\n.custom-widget-box {\\n    margin-top: 20px;\\n    padding: 5px;\\n    border: None !important;\\n}\\n.custom-widget-box > p {\\n    margin: 0px;\\n}\\n.bk-input-group {\\n    color: None !important;\\n}\\n.indicator {\\n    text-align: center;\\n}\\n.widget-card {\\n    margin: 5px 10px;\\n}\\n.number-card {\\n    margin: 5px 10px;\\n    text-align: center;\\n}\\n.number-card-value {\\n    width: 100%;\\n    margin: 0px;\\n}\\n\",{\"id\":\"b17c8d70-a9c4-4944-a08c-09ed41407ad6\"},{\"id\":\"3d632975-aa99-4991-9456-6df7e4303fdb\"}],\"margin\":[5,10],\"sizing_mode\":\"stretch_width\",\"align\":\"start\",\"text\":\"&lt;div style=&quot;font-size: 14pt; color: #2B2B2B&quot;&gt;Datapoints Selected&lt;/div&gt;\\n&lt;div style=&quot;font-size: 18pt; color: #2B2B2B&quot;&gt;3,048,671&lt;/div&gt;\"}},{\"type\":\"object\",\"name\":\"panel.models.widgets.Progress\",\"id\":\"6636c496-1056-48df-b0cd-b7f72afce284\",\"attributes\":{\"styles\":{\"type\":\"map\",\"entries\":[[\"--success-bg-color\",\"#4292c6\"]]},\"stylesheets\":[\"\\n:host(.pn-loading):before, .pn-loading:before {\\n  background-color: #c3c3c3;\\n  mask-size: auto calc(min(50%, 400px));\\n  -webkit-mask-size: auto calc(min(50%, 400px));\\n}\",{\"id\":\"5179eb89-c822-4e9e-9a08-eda5214b7b88\"},\"\\n.dataframe table{\\n  border: none;\\n}\\n\\n.panel-df table{\\n    width: 100%;\\n    border-collapse: collapse;\\n    border: none;\\n}\\n.panel-df td{\\n    white-space: nowrap;\\n    overflow: auto;\\n    text-overflow: ellipsis;\\n}\\n\",\"\\n.multi-select{\\n    color: white;\\n    z-index: 100;\\n    background: rgba(44,43,43,0.5);\\n    border-radius: 1px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n.multi-select > .bk {\\n    padding: 5px;\\n    width: 120px !important;\\n    height: 30px !important;\\n}\\n\\n.deck-chart {\\n    z-index: 10;\\n    position: initial !important;\\n}\\n\",\"\\n.center-header {\\n    text-align: center\\n}\\n.bk-input-group {\\n    padding: 10px;\\n}\\n#sidebar {\\n    padding-top: 10px;\\n}\\n.custom-widget-box {\\n    margin-top: 20px;\\n    padding: 5px;\\n    border: None !important;\\n}\\n.custom-widget-box > p {\\n    margin: 0px;\\n}\\n.bk-input-group {\\n    color: None !important;\\n}\\n.indicator {\\n    text-align: center;\\n}\\n.widget-card {\\n    margin: 5px 10px;\\n}\\n.number-card {\\n    margin: 5px 10px;\\n    text-align: center;\\n}\\n.number-card-value {\\n    width: 100%;\\n    margin: 0px;\\n}\\n\",{\"type\":\"object\",\"name\":\"ImportedStyleSheet\",\"id\":\"7c661704-916e-443b-a063-025d7994f681\",\"attributes\":{\"url\":\"https://cdn.holoviz.org/panel/1.5.2/dist/css/progress.css\"}},{\"id\":\"b17c8d70-a9c4-4944-a08c-09ed41407ad6\"},{\"id\":\"3d632975-aa99-4991-9456-6df7e4303fdb\"}],\"width\":300,\"margin\":[5,10],\"sizing_mode\":\"stretch_width\",\"align\":\"start\",\"bar_color\":\"success\",\"value\":100}}]}},0,0,1,2]]}},{\"type\":\"object\",\"name\":\"panel.models.comm_manager.CommManager\",\"id\":\"74eeb459-6037-428e-8149-248a00c7a1d6\",\"attributes\":{\"plot_id\":\"78fe5a6f-6244-4012-b442-379c3e93ad4c\",\"comm_id\":\"8ca0b25fbb334637af0d27dea7253ce7\",\"client_comm_id\":\"04a112d69a3d41a4be71c6cc0053f34c\"}}],\"defs\":[{\"type\":\"model\",\"name\":\"ReactiveHTML1\"},{\"type\":\"model\",\"name\":\"FlexBox1\",\"properties\":[{\"name\":\"align_content\",\"kind\":\"Any\",\"default\":\"flex-start\"},{\"name\":\"align_items\",\"kind\":\"Any\",\"default\":\"flex-start\"},{\"name\":\"flex_direction\",\"kind\":\"Any\",\"default\":\"row\"},{\"name\":\"flex_wrap\",\"kind\":\"Any\",\"default\":\"wrap\"},{\"name\":\"gap\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"justify_content\",\"kind\":\"Any\",\"default\":\"flex-start\"}]},{\"type\":\"model\",\"name\":\"FloatPanel1\",\"properties\":[{\"name\":\"config\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"contained\",\"kind\":\"Any\",\"default\":true},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"right-top\"},{\"name\":\"offsetx\",\"kind\":\"Any\",\"default\":null},{\"name\":\"offsety\",\"kind\":\"Any\",\"default\":null},{\"name\":\"theme\",\"kind\":\"Any\",\"default\":\"primary\"},{\"name\":\"status\",\"kind\":\"Any\",\"default\":\"normalized\"}]},{\"type\":\"model\",\"name\":\"GridStack1\",\"properties\":[{\"name\":\"mode\",\"kind\":\"Any\",\"default\":\"warn\"},{\"name\":\"ncols\",\"kind\":\"Any\",\"default\":null},{\"name\":\"nrows\",\"kind\":\"Any\",\"default\":null},{\"name\":\"allow_resize\",\"kind\":\"Any\",\"default\":true},{\"name\":\"allow_drag\",\"kind\":\"Any\",\"default\":true},{\"name\":\"state\",\"kind\":\"Any\",\"default\":[]}]},{\"type\":\"model\",\"name\":\"drag1\",\"properties\":[{\"name\":\"slider_width\",\"kind\":\"Any\",\"default\":5},{\"name\":\"slider_color\",\"kind\":\"Any\",\"default\":\"black\"},{\"name\":\"value\",\"kind\":\"Any\",\"default\":50}]},{\"type\":\"model\",\"name\":\"click1\",\"properties\":[{\"name\":\"terminal_output\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"debug_name\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"clears\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"FastWrapper1\",\"properties\":[{\"name\":\"object\",\"kind\":\"Any\",\"default\":null},{\"name\":\"style\",\"kind\":\"Any\",\"default\":null}]},{\"type\":\"model\",\"name\":\"NotificationAreaBase1\",\"properties\":[{\"name\":\"js_events\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"bottom-right\"},{\"name\":\"_clear\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"NotificationArea1\",\"properties\":[{\"name\":\"js_events\",\"kind\":\"Any\",\"default\":{\"type\":\"map\"}},{\"name\":\"notifications\",\"kind\":\"Any\",\"default\":[]},{\"name\":\"position\",\"kind\":\"Any\",\"default\":\"bottom-right\"},{\"name\":\"_clear\",\"kind\":\"Any\",\"default\":0},{\"name\":\"types\",\"kind\":\"Any\",\"default\":[{\"type\":\"map\",\"entries\":[[\"type\",\"warning\"],[\"background\",\"#ffc107\"],[\"icon\",{\"type\":\"map\",\"entries\":[[\"className\",\"fas fa-exclamation-triangle\"],[\"tagName\",\"i\"],[\"color\",\"white\"]]}]]},{\"type\":\"map\",\"entries\":[[\"type\",\"info\"],[\"background\",\"#007bff\"],[\"icon\",{\"type\":\"map\",\"entries\":[[\"className\",\"fas fa-info-circle\"],[\"tagName\",\"i\"],[\"color\",\"white\"]]}]]}]}]},{\"type\":\"model\",\"name\":\"Notification\",\"properties\":[{\"name\":\"background\",\"kind\":\"Any\",\"default\":null},{\"name\":\"duration\",\"kind\":\"Any\",\"default\":3000},{\"name\":\"icon\",\"kind\":\"Any\",\"default\":null},{\"name\":\"message\",\"kind\":\"Any\",\"default\":\"\"},{\"name\":\"notification_type\",\"kind\":\"Any\",\"default\":null},{\"name\":\"_destroyed\",\"kind\":\"Any\",\"default\":false}]},{\"type\":\"model\",\"name\":\"TemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"BootstrapTemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"TemplateEditor1\",\"properties\":[{\"name\":\"layout\",\"kind\":\"Any\",\"default\":[]}]},{\"type\":\"model\",\"name\":\"MaterialTemplateActions1\",\"properties\":[{\"name\":\"open_modal\",\"kind\":\"Any\",\"default\":0},{\"name\":\"close_modal\",\"kind\":\"Any\",\"default\":0}]},{\"type\":\"model\",\"name\":\"ReactiveESM1\"},{\"type\":\"model\",\"name\":\"JSComponent1\"},{\"type\":\"model\",\"name\":\"ReactComponent1\"},{\"type\":\"model\",\"name\":\"AnyWidgetComponent1\"},{\"type\":\"model\",\"name\":\"request_value1\",\"properties\":[{\"name\":\"fill\",\"kind\":\"Any\",\"default\":\"none\"},{\"name\":\"_synced\",\"kind\":\"Any\",\"default\":null},{\"name\":\"_request_sync\",\"kind\":\"Any\",\"default\":0}]}]}};\n",
       "  var render_items = [{\"docid\":\"a4457f2d-fba5-4e12-a508-aa4a084eaa1e\",\"roots\":{\"78fe5a6f-6244-4012-b442-379c3e93ad4c\":\"bfb51cce-757f-42c8-bf92-8f2c7337efaa\"},\"root_ids\":[\"78fe5a6f-6244-4012-b442-379c3e93ad4c\"]}];\n",
       "  var docs = Object.values(docs_json)\n",
       "  if (!docs) {\n",
       "    return\n",
       "  }\n",
       "  const py_version = docs[0].version.replace('rc', '-rc.').replace('.dev', '-dev.')\n",
       "  async function embed_document(root) {\n",
       "    var Bokeh = get_bokeh(root)\n",
       "    await Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "    for (const render_item of render_items) {\n",
       "      for (const root_id of render_item.root_ids) {\n",
       "\tconst id_el = document.getElementById(root_id)\n",
       "\tif (id_el.children.length && id_el.children[0].hasAttribute('data-root-id')) {\n",
       "\t  const root_el = id_el.children[0]\n",
       "\t  root_el.id = root_el.id + '-rendered'\n",
       "\t  for (const child of root_el.children) {\n",
       "            // Ensure JupyterLab does not capture keyboard shortcuts\n",
       "            // see: https://jupyterlab.readthedocs.io/en/4.1.x/extension/notebook.html#keyboard-interaction-model\n",
       "\t    child.setAttribute('data-lm-suppress-shortcuts', 'true')\n",
       "\t  }\n",
       "\t}\n",
       "      }\n",
       "    }\n",
       "  }\n",
       "  function get_bokeh(root) {\n",
       "    if (root.Bokeh === undefined) {\n",
       "      return null\n",
       "    } else if (root.Bokeh.version !== py_version) {\n",
       "      if (root.Bokeh.versions === undefined || !root.Bokeh.versions.has(py_version)) {\n",
       "\treturn null\n",
       "      }\n",
       "      return root.Bokeh.versions.get(py_version);\n",
       "    } else if (root.Bokeh.version === py_version) {\n",
       "      return root.Bokeh\n",
       "    }\n",
       "    return null\n",
       "  }\n",
       "  function is_loaded(root) {\n",
       "    var Bokeh = get_bokeh(root)\n",
       "    return (Bokeh != null && Bokeh.Panel !== undefined && ( root.deck !== undefined) && ( root.deck !== undefined))\n",
       "  }\n",
       "  if (is_loaded(root)) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (is_loaded(root)) {\n",
       "        clearInterval(timer);\n",
       "        embed_document(root);\n",
       "      } else if (document.readyState == \"complete\") {\n",
       "        attempts++;\n",
       "        if (attempts > 200) {\n",
       "          clearInterval(timer);\n",
       "\t  var Bokeh = get_bokeh(root)\n",
       "\t  if (Bokeh == null || Bokeh.Panel == null) {\n",
       "            console.warn(\"Panel: ERROR: Unable to run Panel code because Bokeh or Panel library is missing\");\n",
       "\t  } else {\n",
       "\t    console.warn(\"Panel: WARNING: Attempting to render but not all required libraries could be resolved.\")\n",
       "\t    embed_document(root)\n",
       "\t  }\n",
       "        }\n",
       "      }\n",
       "    }, 25, root)\n",
       "  }\n",
       "})(window);</script>"
      ],
      "text/plain": [
       "GridSpec(ncols=12, nrows=1)\n",
       "    [0] GridSpec(height=800, ncols=12, nrows=5, sizing_mode='fixed', width=1200)\n",
       "        [0] HoloViews(DynamicMap, height=800, sizing_mode='stretch_both', width=1200)\n",
       "    [1] WidgetBox(styles={'border-color': '...})\n",
       "        [0] Number(css_classes=['indicator'], default_color='#2B2B2B', font_size='18pt', format='{value:,}', name='Datapoints Selected', sizing_mode='stretch_width', title_size='14pt', value=3048671)\n",
       "        [1] Progress(sizing_mode='stretch_width', styles={'--success-bg-color': '...}, value=100)"
      ]
     },
     "execution_count": 41,
     "metadata": {
      "application/vnd.holoviews_exec.v0+json": {
       "id": "78fe5a6f-6244-4012-b442-379c3e93ad4c"
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dash = cxf_data.dashboard([heatmap_chart], theme=cxf.themes.dark, data_size_widget=True)\n",
    "\n",
    "dash.app()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'status': 'ok', 'restart': True}"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import IPython\n",
    "app = IPython.Application.instance()\n",
    "app.kernel.do_shutdown(True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Well Done!** Let's move to the [next notebook](2-04_networkx_cugraph.ipynb). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "<img src=\"./images/DLI_Header.png\" width=400/>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
