{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import panel as pn\n", "import pydeck as pdk\n", "pn.extension('deckgl')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to use Deck.gl you need a MAP BOX Key which you can acquire for free for limited use at [mapbox.com](https://account.mapbox.com/access-tokens/).\n", "\n", "Now we can define a JSON spec and pass it to the DeckGL pane along with the Mapbox key:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Advanced Example Based on Global Power Plant Database\n", "\n", "We will base this example on data from the [Global Power Plant Database](http://datasets.wri.org/dataset/540dcf46-f287-47ac-985d-269b04bea4c6/resource/c240ed2e-1190-4d7e-b1da-c66b72e08858/download/globalpowerplantdatabasev120).\n", "\n", "We will create an interactive application to explore the location and types of power plants globally." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import param\n", "import pandas as pd\n", "\n", "POWER_PLANT_URL = (\n", " \"https://raw.githubusercontent.com/MarcSkovMadsen/awesome-streamlit/master/\"\n", " \"gallery/global_power_plant_database/global_power_plant_database.csv\"\n", ")\n", "\n", "MAPBOX_KEY = \"pk.eyJ1IjoicGFuZWxvcmciLCJhIjoiY2s1enA3ejhyMWhmZjNobjM1NXhtbWRrMyJ9.B_frQsAVepGIe-HiOJeqvQ\"\n", "\n", "pp_data = pd.read_csv(POWER_PLANT_URL)\n", "pp_data.head(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Clean the data as PyDeck does not handle NA data well" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pp_data.primary_fuel = pp_data.primary_fuel.fillna(\"NA\")\n", "pp_data.capacity_mw = pp_data.capacity_mw.fillna(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Transform the data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "FUEL_COLORS = {\n", " \"Oil\": \"black\",\n", " \"Solar\": \"green\",\n", " \"Gas\": \"black\",\n", " \"Other\": \"gray\",\n", " \"Hydro\": \"blue\",\n", " \"Coal\": \"black\",\n", " \"Petcoke\": \"black\",\n", " \"Biomass\": \"green\",\n", " \"Waste\": \"green\",\n", " \"Cogeneration\": \"gray\",\n", " \"Storage\": \"orange\",\n", " \"Wind\": \"green\",\n", "}\n", "\n", "COLORS_R = {\"black\": 0, \"green\": 0, \"blue\": 0, \"orange\": 255, \"gray\": 128}\n", "COLORS_G = {\"black\": 0, \"green\": 128, \"blue\": 0, \"orange\": 165, \"gray\": 128}\n", "COLORS_B = {\"black\": 0, \"green\": 0, \"blue\": 255, \"orange\": 0, \"gray\": 128}\n", "\n", "pp_data[\"primary_fuel_color\"] = pp_data.primary_fuel.map(FUEL_COLORS)\n", "pp_data[\"primary_fuel_color\"] = pp_data[\"primary_fuel_color\"].fillna(\"gray\")\n", "pp_data[\"color_r\"] = pp_data[\"primary_fuel_color\"].map(COLORS_R)\n", "pp_data[\"color_g\"] = pp_data[\"primary_fuel_color\"].map(COLORS_G)\n", "pp_data[\"color_b\"] = pp_data[\"primary_fuel_color\"].map(COLORS_B)\n", "pp_data[\"color_a\"] = 140\n", "\n", "# \"name\", \"primary_fuel\", \"capacity_mw\",\n", "pp_data = pp_data[[\n", " \"latitude\",\"longitude\", \"name\", \"capacity_mw\",\n", " \"color_r\",\"color_g\",\"color_b\",\"color_a\",]\n", "]\n", "pp_data.head(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create the app\n", "\n", "Here we create a parameterized class which creates a PyDeck plot in the constructor and connects it to some parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class GlobalPowerPlantDatabaseApp(param.Parameterized):\n", " \n", " data = param.DataFrame(pp_data, precedence=-1)\n", "\n", " opacity = param.Number(default=0.8, step=0.05, bounds=(0, 1))\n", " \n", " pitch = param.Number(default=0, bounds=(0, 90))\n", " \n", " zoom = param.Integer(default=1, bounds=(1, 10))\n", " \n", " def __init__(self, **params):\n", " super(GlobalPowerPlantDatabaseApp, self).__init__(**params)\n", " self._view_state = pdk.ViewState(\n", " latitude=52.2323,\n", " longitude=-1.415,\n", " zoom=self.zoom,\n", " min_zoom=self.param.zoom.bounds[0],\n", " max_zoom=self.param.zoom.bounds[1],\n", " )\n", " self._scatter = pdk.Layer(\n", " \"ScatterplotLayer\",\n", " data=self.data,\n", " get_position=[\"longitude\", \"latitude\"],\n", " get_fill_color=\"[color_r, color_g, color_b, color_a]\",\n", " get_radius=\"capacity_mw*10\",\n", " pickable=True,\n", " opacity=self.opacity,\n", " filled=True,\n", " wireframe=True,\n", " )\n", " self._deck = pdk.Deck(\n", " map_style=\"mapbox://styles/mapbox/light-v9\",\n", " initial_view_state=self._view_state,\n", " layers=[self._scatter],\n", " tooltip=True,\n", " mapbox_key=MAPBOX_KEY,\n", " )\n", " self.pane = pn.pane.DeckGL(self._deck, sizing_mode=\"stretch_width\", height=700)\n", " self.param.watch(self._update, ['data', 'opacity', 'pitch', 'zoom'])\n", "\n", " @pn.depends('pane.hover_state', 'data')\n", " def _info(self):\n", " index = self.pane.hover_state.get('index', -1)\n", " if index == -1:\n", " index = slice(0, 0)\n", " return self.data.iloc[index][['name', 'capacity_mw']]\n", "\n", " @pn.depends('pane.view_State', watch=True)\n", " def _update(self):\n", " state = self.pane.view_state\n", " self._view_state.longitude = state['longitude']\n", " self._view_state.latitude = state['latitude'] \n", "\n", " def _update(self, event):\n", " if event.name == 'data':\n", " self._scatter.data = self.data\n", " if event.name == 'opacity':\n", " self._scatter.opacity = self.opacity\n", " if event.name == 'zoom':\n", " self._view_state.zoom = self.zoom\n", " if event.name == 'pitch':\n", " self._view_state.pitch = self.pitch\n", " self.pane.param.trigger('object')\n", " \n", " def view(self):\n", " return pn.Row(pn.Column(self.param, self._info), self.pane)\n", "\n", "app = GlobalPowerPlantDatabaseApp()\n", "app.view().servable()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Credits\n", "\n", "The DeckGl pane and this reference example was contributed by\n", "\n", "- [Marc Skov Madsen](https://datamodelsanalytics.com)\n", "\n", "and improved by\n", "\n", "- Philipp RĂ¼diger" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.5" } }, "nbformat": 4, "nbformat_minor": 4 }