import panel as pn
import pydeck as pdk
pn.extension('deckgl')
In order to use Deck.gl you need a MAP BOX Key which you can acquire for free for limited use at mapbox.com.
Now we can define a JSON spec and pass it to the DeckGL pane along with the Mapbox key:
We will base this example on data from the Global Power Plant Database.
We will create an interactive application to explore the location and types of power plants globally.
import param
import pandas as pd
POWER_PLANT_URL = (
"https://raw.githubusercontent.com/MarcSkovMadsen/awesome-streamlit/master/"
"gallery/global_power_plant_database/global_power_plant_database.csv"
)
MAPBOX_KEY = "pk.eyJ1IjoicGFuZWxvcmciLCJhIjoiY2s1enA3ejhyMWhmZjNobjM1NXhtbWRrMyJ9.B_frQsAVepGIe-HiOJeqvQ"
pp_data = pd.read_csv(POWER_PLANT_URL)
pp_data.head(1)
Clean the data as PyDeck does not handle NA data well
pp_data.primary_fuel = pp_data.primary_fuel.fillna("NA")
pp_data.capacity_mw = pp_data.capacity_mw.fillna(1)
FUEL_COLORS = {
"Oil": "black",
"Solar": "green",
"Gas": "black",
"Other": "gray",
"Hydro": "blue",
"Coal": "black",
"Petcoke": "black",
"Biomass": "green",
"Waste": "green",
"Cogeneration": "gray",
"Storage": "orange",
"Wind": "green",
}
COLORS_R = {"black": 0, "green": 0, "blue": 0, "orange": 255, "gray": 128}
COLORS_G = {"black": 0, "green": 128, "blue": 0, "orange": 165, "gray": 128}
COLORS_B = {"black": 0, "green": 0, "blue": 255, "orange": 0, "gray": 128}
pp_data["primary_fuel_color"] = pp_data.primary_fuel.map(FUEL_COLORS)
pp_data["primary_fuel_color"] = pp_data["primary_fuel_color"].fillna("gray")
pp_data["color_r"] = pp_data["primary_fuel_color"].map(COLORS_R)
pp_data["color_g"] = pp_data["primary_fuel_color"].map(COLORS_G)
pp_data["color_b"] = pp_data["primary_fuel_color"].map(COLORS_B)
pp_data["color_a"] = 140
# "name", "primary_fuel", "capacity_mw",
pp_data = pp_data[[
"latitude","longitude", "name", "capacity_mw",
"color_r","color_g","color_b","color_a",]
]
pp_data.head(1)
Here we create a parameterized class which creates a PyDeck plot in the constructor and connects it to some parameters.
class GlobalPowerPlantDatabaseApp(param.Parameterized):
data = param.DataFrame(pp_data, precedence=-1)
opacity = param.Number(default=0.8, step=0.05, bounds=(0, 1))
pitch = param.Number(default=0, bounds=(0, 90))
zoom = param.Integer(default=1, bounds=(1, 10))
def __init__(self, **params):
super(GlobalPowerPlantDatabaseApp, self).__init__(**params)
self._view_state = pdk.ViewState(
latitude=52.2323,
longitude=-1.415,
zoom=self.zoom,
min_zoom=self.param.zoom.bounds[0],
max_zoom=self.param.zoom.bounds[1],
)
self._scatter = pdk.Layer(
"ScatterplotLayer",
data=self.data,
get_position=["longitude", "latitude"],
get_fill_color="[color_r, color_g, color_b, color_a]",
get_radius="capacity_mw*10",
pickable=True,
opacity=self.opacity,
filled=True,
wireframe=True,
)
self._deck = pdk.Deck(
map_style="mapbox://styles/mapbox/light-v9",
initial_view_state=self._view_state,
layers=[self._scatter],
tooltip=True,
mapbox_key=MAPBOX_KEY,
)
self.pane = pn.pane.DeckGL(self._deck, sizing_mode="stretch_width", height=700)
self.param.watch(self._update, ['data', 'opacity', 'pitch', 'zoom'])
@pn.depends('pane.hover_state', 'data')
def _info(self):
index = self.pane.hover_state.get('index', -1)
if index == -1:
index = slice(0, 0)
return self.data.iloc[index][['name', 'capacity_mw']]
@pn.depends('pane.view_State', watch=True)
def _update(self):
state = self.pane.view_state
self._view_state.longitude = state['longitude']
self._view_state.latitude = state['latitude']
def _update(self, event):
if event.name == 'data':
self._scatter.data = self.data
if event.name == 'opacity':
self._scatter.opacity = self.opacity
if event.name == 'zoom':
self._view_state.zoom = self.zoom
if event.name == 'pitch':
self._view_state.pitch = self.pitch
self.pane.param.trigger('object')
def view(self):
return pn.Row(pn.Column(self.param, self._info), self.pane)
app = GlobalPowerPlantDatabaseApp()
app.view().servable()
The DeckGl pane and this reference example was contributed by
and improved by