pydeck
pydeck is the Python binding for deck.gl, a WebGL visualization framework for large spatial data sets. Python code describes layers, views, tooltips, and camera state, then serializes that specification and its data to JavaScript for rendering in a browser or notebook. It is good at interactive maps and GPU-accelerated visual aggregation; it is not a server-side chart renderer or a GIS analysis engine.
pydeck is a strong bridge from Python data frames to deck.gl when an interactive GPU map is the actual deliverable. Avoid it for headless rendering, GIS processing, or any workflow where browser and basemap dependencies are unacceptable.
Use it if
- You need interactive point, arc, polygon, path, heatmap, hexagon, or trip visualizations that benefit from browser GPU rendering
- Your data work happens in Python or a notebook but the output should behave like a deck.gl web map
- You want to export a self-contained HTML visualization without building a React application
- You already understand deck.gl layer properties and want a thin Python representation rather than a separate plotting grammar
- You need static PNG, SVG, or publication plots on a headless server: pydeck emits a browser visualization and needs WebGL, while Matplotlib or Plotly image export fits that job better
- You expect Python callback functions for every hover or click: the layer specification is serialized to JavaScript, so notebook interactivity is not equivalent to a live Python event loop
- Your deliverable must work offline with arbitrary basemaps: map styles and tiles often come from remote providers and may require a token, even when the overlay data is embedded
- You plan to embed millions of raw rows into one HTML file: browser GPU drawing can be fast, but Python still serializes the data and the browser still downloads, parses, and stores it
- You need spatial joins, reprojection, topology repair, or geocoding: pydeck renders already prepared data and does not replace GeoPandas, pyproj, Shapely, or a geocoder
Setup reality
pip install pydeck brings Jinja2 and NumPy and works for basic HTML output, but the visible result is JavaScript running in a WebGL-capable browser. Version 0.9.3 supports Python 3.8 and newer. Notebook support is where setup becomes uneven: the published jupyter extra requests ipywidgets 7.x, while many current notebook environments use widgets 8, so check the environment before blindly installing pydeck[jupyter]. The notebook, VS Code, Colab, and Streamlit each have different display plumbing, security policies, and persistence behavior. Basemap configuration is separate from overlay layers. Some map styles require Mapbox credentials, remote style URLs need network access, and a blank background can be a token, CSP, WebGL, or tile-provider problem rather than a layer bug. Accessor strings such as get_position are interpreted by deck.gl against serialized row fields; they are not Python expressions. Coordinates are normally longitude and latitude, while elevation and radius units vary by layer property. Large frames can make notebook messages and exported HTML enormous before the GPU gets involved, so aggregate or use URL/binary data paths when scale demands it. Tooltips use an HTML template and receive browser-side object fields, which means untrusted text should not be inserted as raw HTML. For repeatable exports, pin pydeck and test the generated file in the browser versions you support because the Python package ships or references a particular deck.gl frontend bundle.
Patterns
Render a basic scatterplot mapscatterplot-map
import pydeck as pdk
data = [{'lon': -122.4, 'lat': 37.78, 'count': 120}]
layer = pdk.Layer(
'ScatterplotLayer', data,
get_position='[lon, lat]', get_radius=100,
get_fill_color=[30, 136, 229, 180], pickable=True,
)
deck = pdk.Deck(layers=[layer], initial_view_state=pdk.ViewState(
longitude=-122.4, latitude=37.78, zoom=11
))
deck.show()Coordinate accessors use longitude then latitude; get_position is evaluated by deck.gl against each serialized row.
Show row fields in a tooltipadd-tooltip
deck = pdk.Deck(
layers=[layer],
initial_view_state=view_state,
tooltip={'html': '<b>{name}</b><br/>Count: {count}', 'style': {'color': 'white'}},
)Tooltip placeholders refer to serialized object fields; do not put untrusted values into raw HTML without sanitizing them first.
Aggregate dense points into hexagonsaggregate-hexagons
hexes = pdk.Layer(
'HexagonLayer', data,
get_position='[lon, lat]',
radius=250, elevation_scale=20,
extruded=True, pickable=True,
)Aggregation happens in the browser; sending every raw point can still dominate load time even when GPU drawing is quick.
Draw GeoJSON polygons and linesdraw-geojson
geo = pdk.Layer(
'GeoJsonLayer', geojson,
filled=True, stroked=True, pickable=True,
get_fill_color=[0, 128, 255, 90],
get_line_color=[20, 20, 20],
line_width_min_pixels=1,
)GeoJSON coordinates should be WGS84 longitude and latitude; pydeck does not reproject source geometry for you.
Connect origins and destinations with arcsconnect-points-with-arcs
arcs = pdk.Layer(
'ArcLayer', trips,
get_source_position='source',
get_target_position='target',
get_source_color=[0, 180, 255],
get_target_color=[255, 80, 80],
get_width=3, pickable=True,
)Each source and target value must already be a two-number coordinate array in longitude, latitude order.
Render routes with PathLayerrender-paths
paths = pdk.Layer(
'PathLayer', routes,
get_path='path', get_color='color',
width_scale=4, width_min_pixels=2,
get_width=2, pickable=True,
)A path is an array of coordinate arrays; simplify very detailed lines before serialization when browser payload is the bottleneck.
Create a weighted heatmapvisualize-heatmap
heat = pdk.Layer(
'HeatmapLayer', points,
get_position='[longitude, latitude]',
get_weight='magnitude',
radius_pixels=40,
)radius_pixels is a screen-space smoothing radius, not meters, so the appearance changes as the camera zooms.
Render timestamped moving pathsanimate-trips
trips_layer = pdk.Layer(
'TripsLayer', trips,
get_path='waypoints.map(d => d.coordinates)',
get_timestamps='waypoints.map(d => d.timestamp)',
get_color=[253, 128, 93],
current_time=500, trail_length=180,
)pydeck can describe TripsLayer, but a standalone Python export does not create an animation loop; the current_time property must be updated by a frontend or widget.
Choose a basemap styleset-map-style
deck = pdk.Deck(
layers=[layer],
initial_view_state=view_state,
map_style='light',
)Style availability, remote tiles, and credentials depend on the selected map provider; the overlay can render even when the basemap fails.
Render layers without map tilesrender-without-basemap
deck = pdk.Deck(
layers=[layer],
initial_view_state=view_state,
map_style=None,
)This removes provider and token requirements but leaves only the deck.gl canvas, so add enough geographic context in your own layers.
Write an interactive HTML fileexport-html
deck.to_html(
'map.html',
open_browser=False,
notebook_display=False,
)Embedded data can make the HTML very large, and remote basemap styles or tiles still need network access unless you remove them.
Set camera bearing and pitchfit-camera-manually
view_state = pdk.ViewState(
longitude=-73.98, latitude=40.75,
zoom=10, pitch=45, bearing=-20,
)
deck = pdk.Deck(layers=[layer], initial_view_state=view_state)ViewState is an initial camera, not automatic bounds fitting; calculate a suitable center and zoom when the data extent varies.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| plotly | PyPI | You need a broader charting system with maps plus conventional statistical charts and static export options |
| folium | PyPI | You want Leaflet maps, markers, and plugins and do not need deck.gl's GPU layer model |
| ipyleaflet | PyPI | You need two-way Jupyter widget interaction around a Leaflet map |