{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Demonstrates the use of [`Folium`](https://python-visualization.github.io/folium/) in Panel."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import folium as fm\n",
    "import pandas as pd\n",
    "import param\n",
    "import panel as pn\n",
    "import random\n",
    "pn.extension(sizing_mode=\"stretch_width\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can use `Folium` in a Panel App"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_map(lat=20.5936832, long=78.962883, zoom_start=5):\n",
    "    return fm.Map(location=[lat,long], zoom_start=zoom_start)\n",
    "\n",
    "map = get_map()\n",
    "\n",
    "pn.panel(map, height=400)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Interactive Panel app using Folium\n",
    "\n",
    "Let build an interactive Panel application using Folium.\n",
    "\n",
    "Lets **define some data**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_df_aqi(lat_bounds=(10,30), long_bounds=(70,90), points_count=40):\n",
    "    aqi = {\n",
    "        \"Latitude\": [random.uniform(*lat_bounds) for _ in range(0,points_count)],\n",
    "        \"Longitude\": [random.uniform(*long_bounds) for _ in range(0,points_count)],\n",
    "        \"AQI\": [random.uniform(0,500) for _ in range(0,points_count)],\n",
    "    }\n",
    "    return pd.DataFrame(aqi)\n",
    "\n",
    "df_aqi = get_df_aqi()\n",
    "df_aqi.sample(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lets define some functionality to **add the data to the map** as circles"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_aqi_circles(map, df_aqi):\n",
    "    green_p1  = fm.map.FeatureGroup()\n",
    "    yellow_p1 = fm.map.FeatureGroup()\n",
    "    orange_p1 = fm.map.FeatureGroup()\n",
    "    red_p1    = fm.map.FeatureGroup()\n",
    "    purple_p1 = fm.map.FeatureGroup()\n",
    "    maroon_p1 = fm.map.FeatureGroup()\n",
    "\n",
    "    for _, row in df_aqi.iterrows():\n",
    "        if row.AQI<50:\n",
    "            feature_group = green_p1\n",
    "            fill_color = \"green\"\n",
    "        elif row.AQI < 100:\n",
    "            feature_group = yellow_p1\n",
    "            fill_color = \"yellow\"\n",
    "        elif row.AQI < 150:\n",
    "            feature_group = orange_p1\n",
    "            fill_color = \"orange\"\n",
    "        elif row.AQI < 200:\n",
    "            feature_group = red_p1\n",
    "            fill_color = \"red\"\n",
    "        elif row.AQI < 300:\n",
    "            feature_group = purple_p1\n",
    "            fill_color='purple'\n",
    "        else:\n",
    "            feature_group = maroon_p1\n",
    "            fill_color = \"maroon\"\n",
    "\n",
    "        feature_group.add_child(\n",
    "            fm.CircleMarker(\n",
    "                [row.Latitude, row.Longitude],\n",
    "                radius=10, \n",
    "                fill=True,\n",
    "                fill_color=fill_color,\n",
    "                fill_opacity=0.7\n",
    "            )\n",
    "        )\n",
    "\n",
    "    map.add_child(green_p1)\n",
    "    map.add_child(yellow_p1)\n",
    "    map.add_child(orange_p1)\n",
    "    map.add_child(red_p1)\n",
    "    map.add_child(purple_p1)\n",
    "\n",
    "add_aqi_circles(map, df_aqi)\n",
    "pn.panel(map, height=400)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lets put it all together into an **interactive app** where the user can select the number of data points to generate and display."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class PanelFoliumMap(param.Parameterized):\n",
    "    points_count = param.Integer(20, bounds=(10,100))\n",
    "        \n",
    "    def __init__(self, **params):\n",
    "        super().__init__(**params)\n",
    "        self.map = get_map()\n",
    "        self.folium_pane = pn.pane.plot.Folium(sizing_mode=\"stretch_both\", min_height=500, margin=0)    \n",
    "        self.view = pn.Column(\n",
    "            self.param.points_count,\n",
    "            self.folium_pane,\n",
    "            sizing_mode=\"stretch_both\", height=500\n",
    "        )\n",
    "        self._update_map()\n",
    "\n",
    "    @param.depends(\"points_count\", watch=True)\n",
    "    def _update_map(self):\n",
    "        self.map = get_map()\n",
    "        df_aqi = get_df_aqi(points_count=self.points_count)\n",
    "        add_aqi_circles(self.map, df_aqi)\n",
    "        self.folium_pane.object = self.map\n",
    "\n",
    "        \n",
    "app = PanelFoliumMap()\n",
    "app.view"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## App\n",
    "\n",
    "Lets wrap it into nice template that can be served via `panel serve Folium.ipynb`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.template.FastListTemplate(site=\"Panel\", title=\"Folium\", main=[\"This app demonstrates **how to use Folium with Panel**.\", PanelFoliumMap().view]).servable();"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
