Nyc Deckgl#
Explore NYC taxi pickups by hour with a deck.gl HexagonLayer. Click on the map to show arcs from the pickups near that point to their dropoffs.
import pandas as pd
import panel as pn
import param
pn.extension('deckgl')
Load data#
@pn.cache
def load_taxi():
return pd.read_parquet('https://datasets.holoviz.org/nyc_taxi_small/v1/nyc_taxi_small.parq')
EMPIRE_STATE = (-73.9857, 40.7484)
def hourly(data, hour):
return data[data.hour == hour]
def arcs(data, coordinate, radius):
lon, lat = coordinate
tol = radius / 100000
return data[
data.pickup_x.between(lon - tol, lon + tol) &
data.pickup_y.between(lat - tol, lat + tol)
]
Define App#
The deck.gl spec is a param.depends method bound into a single DeckGL pane. Clicks on the map are mirrored into the click_state parameter, so the arcs are derived from parameters like everything else, and a Player advances the hour.
class App(pn.viewable.Viewer):
data = param.DataFrame(precedence=-1)
radius = param.Integer(default=50, bounds=(20, 1000))
elevation = param.Integer(default=10, bounds=(0, 50))
hour = param.Integer(default=0, bounds=(0, 23))
click_state = param.Dict(default={}, precedence=-1)
def __init__(self, **params):
super().__init__(**params)
self.deck = pn.ui.DeckGL(
self.spec,
throttle={'click': 10},
sizing_mode='stretch_both',
margin=0,
min_height=800
)
self.deck.param.watch(lambda event: setattr(self, 'click_state', event.new), 'click_state')
self.controls = pn.ui.Column(
pn.ui.IntSlider.from_param(self.param.radius),
pn.ui.IntSlider.from_param(self.param.elevation),
pn.ui.Player.from_param(self.param.hour, interval=1000, loop_policy='loop', show_value=True),
)
@param.depends('data', 'hour')
def view(self):
return hourly(self.data, self.hour)
@param.depends('data', 'hour', 'radius', 'click_state')
def arc_view(self):
return arcs(self.view(), self.click_state.get('coordinate', EMPIRE_STATE), self.radius)
@param.depends('data', 'hour', 'radius', 'elevation', 'click_state')
def spec(self):
return {
"initialViewState": {
"bearing": 0,
"latitude": 40.7,
"longitude": -73.9,
"maxZoom": 15,
"minZoom": 5,
"pitch": 40.5,
"zoom": 11
},
"mapStyle": "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
"layers": [{
"@@type": "HexagonLayer",
"autoHighlight": True,
"coverage": 1,
"data": self.view(),
"elevationRange": [0, 100],
"elevationScale": self.elevation,
"radius": self.radius,
"extruded": True,
"getPosition": "@@=[pickup_x, pickup_y]",
"id": "hexagons",
}, {
"@@type": "ArcLayer",
"id": 'arc-layer',
"data": self.arc_view(),
"pickable": True,
"getWidth": 2,
"getSourcePosition": "@@=[pickup_x, pickup_y]",
"getTargetPosition": "@@=[dropoff_x, dropoff_y]",
"getSourceColor": [0, 255, 0, 180],
"getTargetColor": [240, 100, 0, 180]
}],
"views": [
{"@@type": "MapView", "controller": True}
]
}
def __panel__(self):
return pn.ui.Page(
main=[self.deck],
sidebar=[self.controls],
sidebar_width=350,
title='NYC Taxi Deck.GL Explorer',
sx={".MuiToolbar-root": {"mb": 0}}
)
Display app#
app = App(data=load_taxi())
app
Served App#
if pn.state.served:
app.servable()